diff options
author | Fathi Boudra <fabo@debian.org> | 2010-01-31 11:04:40 +0100 |
---|---|---|
committer | Fathi Boudra <fabo@debian.org> | 2010-01-31 11:04:40 +0100 |
commit | a1896bbe8fa68dac87fa2bb78dd7f83d93b2b552 (patch) | |
tree | 841889ecbedb1aab55f5b3e131f344ac572faa09 /debian/patches | |
parent | ac34295f68690de3783233e9d291ab941feb75f3 (diff) | |
download | qt4-x11-a1896bbe8fa68dac87fa2bb78dd7f83d93b2b552.tar.gz |
Add patch to update Qt Phonon from kdesupport Phonon 4.3.80.
Diffstat (limited to 'debian/patches')
-rw-r--r-- | debian/patches/97_update_phonon_4.3.80.diff | 5608 | ||||
-rw-r--r-- | debian/patches/series | 1 |
2 files changed, 5609 insertions, 0 deletions
diff --git a/debian/patches/97_update_phonon_4.3.80.diff b/debian/patches/97_update_phonon_4.3.80.diff new file mode 100644 index 0000000..8b943df --- /dev/null +++ b/debian/patches/97_update_phonon_4.3.80.diff @@ -0,0 +1,5608 @@ +Description: update Qt Phonon from kdesupport Phonon 4.3.80. +To create the following patch, use the following command excluding some patterns +(DESIGN cmake ds9 experimental mmf qt7 tests xine) +$ diff -Nur -X phonon.exclude qt4-x11-4.6.1/src/3rdparty/phonon phonon-4.3.80 +Forwarded: not-needed +Author: Fathi Boudra <fabo@debian.org> + +--- a/src/3rdparty/phonon/CMakeLists.txt ++++ b/src/3rdparty/phonon/CMakeLists.txt +@@ -149,7 +149,7 @@ set(CMAKE_COLOR_MAKEFILE ON) + + set(PHONON_LIB_MAJOR_VERSION "4") + set(PHONON_LIB_MINOR_VERSION "3") +-set(PHONON_LIB_PATCH_VERSION "50") ++set(PHONON_LIB_PATCH_VERSION "80") + set(PHONON_LIB_VERSION "${PHONON_LIB_MAJOR_VERSION}.4.0") + set(PHONON_LIB_SOVERSION ${PHONON_LIB_MAJOR_VERSION}) + +--- a/src/3rdparty/phonon/gstreamer/abstractrenderer.cpp ++++ b/src/3rdparty/phonon/gstreamer/abstractrenderer.cpp +@@ -17,7 +17,6 @@ + + #include "abstractrenderer.h" + +-#ifndef QT_NO_PHONON_VIDEO + QT_BEGIN_NAMESPACE + + namespace Phonon +@@ -53,5 +52,5 @@ void AbstractRenderer::movieSizeChanged( + } //namespace Phonon::Gstreamer + + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_VIDEO ++ + +--- a/src/3rdparty/phonon/gstreamer/abstractrenderer.h ++++ b/src/3rdparty/phonon/gstreamer/abstractrenderer.h +@@ -23,7 +23,6 @@ + #include "medianode.h" + #include <phonon/videowidget.h> + +-#ifndef QT_NO_PHONON_VIDEO + QT_BEGIN_NAMESPACE + + class QString; +@@ -59,5 +58,5 @@ protected: + } //namespace Phonon::Gstreamer + + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_VIDEO ++ + #endif // Phonon_GSTREAMER_ABSTRACTRENDERER_H +--- a/src/3rdparty/phonon/gstreamer/artssink.cpp ++++ b/src/3rdparty/phonon/gstreamer/artssink.cpp +@@ -233,7 +233,7 @@ static void arts_sink_init (ArtsSink * s + Q_UNUSED(g_class); + GST_DEBUG_OBJECT (src, "initializing artssink"); + src->stream = 0; +-#ifndef QT_NO_LIBRARY ++ + p_arts_init = (Ptr_arts_init)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_init"); + p_arts_play_stream = (Ptr_arts_play_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_play_stream"); + p_arts_close_stream = (Ptr_arts_close_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_close_stream"); +@@ -250,7 +250,6 @@ static void arts_sink_init (ArtsSink * s + } + } + sinkCount ++; +-#endif //QT_NO_LIBRARY + } + + static void arts_sink_dispose (GObject * object) +--- /dev/null ++++ b/gstreamer/audiodataoutput.cpp +@@ -0,0 +1,143 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2006 Matthias Kretz <kretz@kde.org> ++ Copyright (C) 2009 Martin Sandsmark <sandsmark@samfundet.no> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#include "audiodataoutput.h" ++#include "gsthelper.h" ++#include "medianode.h" ++#include "mediaobject.h" ++#include <QtCore/QVector> ++#include <QtCore/QMap> ++#include <phonon/audiooutput.h> ++ ++namespace Phonon ++{ ++namespace Gstreamer ++{ ++AudioDataOutput::AudioDataOutput(Backend *backend, QObject *parent) ++ : QObject(parent), ++ MediaNode(backend, AudioSink | AudioSource) ++{ ++ static int count = 0; ++ m_name = "AudioDataOutput" + QString::number(count++); ++ ++ m_queue = gst_element_factory_make ("identity", NULL); ++ gst_object_ref(m_queue); ++ m_isValid = true; ++} ++ ++AudioDataOutput::~AudioDataOutput() ++{ ++ gst_element_set_state(m_queue, GST_STATE_NULL); ++ gst_object_unref(m_queue); ++} ++ ++int AudioDataOutput::dataSize() const ++{ ++ return m_dataSize; ++} ++ ++int AudioDataOutput::sampleRate() const ++{ ++ return 44100; ++} ++ ++void AudioDataOutput::setDataSize(int size) ++{ ++ m_dataSize = size; ++} ++ ++typedef QMap<Phonon::AudioDataOutput::Channel, QVector<float> > FloatMap; ++typedef QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > IntMap; ++ ++inline void AudioDataOutput::convertAndEmit(const QVector<qint16> &leftBuffer, const QVector<qint16> &rightBuffer) ++{ ++ //TODO: Floats ++ IntMap map; ++ map.insert(Phonon::AudioDataOutput::LeftChannel, leftBuffer); ++ map.insert(Phonon::AudioDataOutput::RightChannel, rightBuffer); ++ emit dataReady(map); ++} ++ ++void AudioDataOutput::processBuffer(GstPad*, GstBuffer* buffer, gpointer gThat) ++{ ++ // TODO emit endOfMedia ++ AudioDataOutput *that = reinterpret_cast<AudioDataOutput*>(gThat); ++ ++ // determine the number of channels ++ GstStructure* structure = gst_caps_get_structure (GST_BUFFER_CAPS(buffer), 0); ++ gst_structure_get_int (structure, "channels", &that->m_channels); ++ ++ if (that->m_channels > 2 || that->m_channels < 0) { ++ qWarning() << Q_FUNC_INFO << ": Number of channels not supported: " << that->m_channels; ++ return; ++ } ++ ++ gint16 *data = reinterpret_cast<gint16*>(GST_BUFFER_DATA(buffer)); ++ guint size = GST_BUFFER_SIZE(buffer) / sizeof(gint16); ++ ++ that->m_pendingData.reserve(that->m_pendingData.size() + size); ++ ++ for (uint i=0; i<size; i++) { ++ // 8 bit? interleaved? yay for lacking documentation! ++ that->m_pendingData.append(data[i]); ++ } ++ ++ while (that->m_pendingData.size() > that->m_dataSize * that->m_channels) { ++ if (that->m_channels == 1) { ++ QVector<qint16> intBuffer(that->m_dataSize); ++ memcpy(intBuffer.data(), that->m_pendingData.constData(), that->m_dataSize * sizeof(qint16)); ++ ++ that->convertAndEmit(intBuffer, intBuffer); ++ int newSize = that->m_pendingData.size() - that->m_dataSize; ++ memmove(that->m_pendingData.data(), that->m_pendingData.constData() + that->m_dataSize, newSize * sizeof(qint16)); ++ that->m_pendingData.resize(newSize); ++ } else { ++ QVector<qint16> left(that->m_dataSize), right(that->m_dataSize); ++ for (int i=0; i<that->m_dataSize; i++) { ++ left[i] = that->m_pendingData[i*2]; ++ right[i] = that->m_pendingData[i*2+1]; ++ } ++ that->m_pendingData.resize(that->m_pendingData.size() - that->m_dataSize*2); ++ that->convertAndEmit(left, right); ++ } ++ } ++} ++ ++void AudioDataOutput::mediaNodeEvent(const MediaNodeEvent *event) ++{ ++ if (event->type() == MediaNodeEvent::MediaObjectConnected && root()) { ++ g_object_set(G_OBJECT(audioElement()), "sync", true, (const char*)NULL); ++ GstPad *audiopad = gst_element_get_pad (audioElement(), "src"); ++ gst_pad_add_buffer_probe (audiopad, G_CALLBACK(processBuffer), this); ++ gst_object_unref (audiopad); ++ return; ++ } ++ ++ MediaNode::mediaNodeEvent(event); ++} ++ ++}} //namespace Phonon::Gstreamer ++ ++#include "moc_audiodataoutput.cpp" ++// vim: sw=4 ts=4 ++ +--- /dev/null ++++ b/gstreamer/audiodataoutput.h +@@ -0,0 +1,84 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2006 Matthias Kretz <kretz@kde.org> ++ Copyright (C) 2009 Martin Sandsmark <sandsmark@samfundet.no> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++*/ ++ ++#ifndef Phonon_GSTREAMER_AUDIODATAOUTPUT_H ++#define Phonon_GSTREAMER_AUDIODATAOUTPUT_H ++ ++#include "abstractaudiooutput.h" ++#include "backend.h" ++#include "medianode.h" ++#include <phonon/audiodataoutput.h> ++#include <phonon/audiodataoutputinterface.h> ++ ++namespace Phonon ++{ ++namespace Gstreamer ++{ ++ /** ++ * \author Martin Sandsmark <sandsmark@samfundet.no> ++ */ ++ class AudioDataOutput : public QObject, ++ public AudioDataOutputInterface, ++ public MediaNode ++ { ++ Q_OBJECT ++ Q_INTERFACES(Phonon::AudioDataOutputInterface Phonon::Gstreamer::MediaNode) ++ ++ public: ++ AudioDataOutput(Backend *, QObject *); ++ ~AudioDataOutput(); ++ ++ public Q_SLOTS: ++ int dataSize() const; ++ int sampleRate() const; ++ void setDataSize(int size); ++ ++ public: ++ /// callback function for handling new audio data ++ static void processBuffer(GstPad*, GstBuffer*, gpointer); ++ ++ Phonon::AudioDataOutput* frontendObject() const { return m_frontend; } ++ void setFrontendObject(Phonon::AudioDataOutput *frontend) { m_frontend = frontend; } ++ ++ GstElement *audioElement() { return m_queue; } ++ ++ void mediaNodeEvent(const MediaNodeEvent *event); ++ ++ ++ signals: ++ void dataReady(const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &data); ++ void dataReady(const QMap<Phonon::AudioDataOutput::Channel, QVector<float> > &data); ++ void endOfMedia(int remainingSamples); ++ ++ private: ++ void convertAndEmit(const QVector<qint16>&, const QVector<qint16>&); ++ ++ GstElement *m_queue; ++ int m_dataSize; ++ QVector<qint16> m_pendingData; ++ Phonon::AudioDataOutput *m_frontend; ++ int m_channels; ++ }; ++}} //namespace Phonon::Gstreamer ++ ++// vim: sw=4 ts=4 tw=80 ++#endif // Phonon_FAKE_AUDIODATAOUTPUT_H +--- a/src/3rdparty/phonon/gstreamer/audioeffect.cpp ++++ b/src/3rdparty/phonon/gstreamer/audioeffect.cpp +@@ -23,7 +23,7 @@ + #include "gsthelper.h" + + #include <gst/gst.h> +-#ifndef QT_NO_PHONON_EFFECT ++ + QT_BEGIN_NAMESPACE + + namespace Phonon +@@ -75,5 +75,4 @@ GstElement* AudioEffect::createEffectBin + } //namespace Phonon::Gstreamer + + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_EFFECT + #include "moc_audioeffect.cpp" +--- a/src/3rdparty/phonon/gstreamer/audioeffect.h ++++ b/src/3rdparty/phonon/gstreamer/audioeffect.h +@@ -29,8 +29,8 @@ + + #include <gst/gst.h> + +-#ifndef QT_NO_PHONON_EFFECT + QT_BEGIN_NAMESPACE ++ + namespace Phonon + { + namespace Gstreamer +@@ -49,7 +49,7 @@ namespace Gstreamer + QString m_effectName; + }; + }} //namespace Phonon::Gstreamer ++ + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_EFFECT + + #endif // Phonon_GSTREAMER_AUDIOEFFECT_H +--- a/src/3rdparty/phonon/gstreamer/audiooutput.cpp ++++ b/src/3rdparty/phonon/gstreamer/audiooutput.cpp +@@ -125,6 +125,7 @@ void AudioOutput::setVolume(qreal newVol + bool AudioOutput::setOutputDevice(int newDevice) + { + m_backend->logMessage(Q_FUNC_INFO + QString::number(newDevice), Backend::Info, this); ++ + if (newDevice == m_device) + return true; + +@@ -135,20 +136,11 @@ bool AudioOutput::setOutputDevice(int ne + } + + bool success = false; +- const QList<AudioDevice> deviceList = m_backend->deviceManager()->audioOutputDevices(); +- int deviceIdx = -1; +- for (int i=0; i<deviceList.size(); i++) { +- if (deviceList.at(i).id == newDevice) { +- deviceIdx = i; +- break; +- } +- } +- +- if (m_audioSink && deviceIdx >= 0) { ++ if (m_audioSink && newDevice >= 0) { + // Save previous state + GstState oldState = GST_STATE(m_audioSink); + const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device"); +- const QByteArray deviceId = deviceList.at(deviceIdx).gstId; ++ const QByteArray deviceId = m_backend->deviceManager()->gstId(newDevice); + m_device = newDevice; + + // We test if the device can be opened by checking if it can go from NULL to READY state +--- a/src/3rdparty/phonon/gstreamer/backend.cpp ++++ b/src/3rdparty/phonon/gstreamer/backend.cpp +@@ -18,6 +18,7 @@ + #include "common.h" + #include "backend.h" + #include "audiooutput.h" ++#include "audiodataoutput.h" + #include "audioeffect.h" + #include "mediaobject.h" + #include "videowidget.h" +@@ -26,6 +27,7 @@ + #include "message.h" + #include "volumefadereffect.h" + #include <gst/interfaces/propertyprobe.h> ++#include <phonon/pulsesupport_p.h> + + #include <QtCore/QSet> + #include <QtCore/QVariant> +@@ -49,26 +51,28 @@ Backend::Backend(QObject *parent, const + , m_debugLevel(Warning) + , m_isValid(false) + { ++ // Initialise PulseAudio support ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ connect(pulse, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), SIGNAL(objectDescriptionChanged(ObjectDescriptionType))); ++ + // In order to support reloading, we only set the app name once... + static bool first = true; + if (first) { + first = false; + g_set_application_name(qApp->applicationName().toUtf8()); + } +- + GError *err = 0; + bool wasInit = gst_init_check(0, 0, &err); //init gstreamer: must be called before any gst-related functions + if (err) + g_error_free(err); + + qRegisterMetaType<Message>("Message"); +-#ifndef QT_NO_PROPERTIES ++ + setProperty("identifier", QLatin1String("phonon_gstreamer")); + setProperty("backendName", QLatin1String("Gstreamer")); + setProperty("backendComment", QLatin1String("Gstreamer plugin for Phonon")); + setProperty("backendVersion", QLatin1String("0.2")); +- setProperty("backendWebsite", QLatin1String("http://qt.nokia.com/")); +-#endif //QT_NO_PROPERTIES ++ setProperty("backendWebsite", QLatin1String("http://qtsoftware.com/")); + + //check if we should enable debug output + QString debugLevelString = qgetenv("PHONON_GST_DEBUG"); +@@ -92,6 +96,9 @@ Backend::Backend(QObject *parent, const + + Backend::~Backend() + { ++ delete m_effectManager; ++ delete m_deviceManager; ++ PulseSupport::shutdown(); + } + + gboolean Backend::busCall(GstBus *bus, GstMessage *msg, gpointer data) +@@ -119,20 +126,15 @@ QObject *Backend::createObject(BackendIn + case MediaObjectClass: + return new MediaObject(this, parent); + +- case AudioOutputClass: { +- AudioOutput *ao = new AudioOutput(this, parent); +- m_audioOutputs.append(ao); +- return ao; +- } +-#ifndef QT_NO_PHONON_EFFECT ++ case AudioOutputClass: ++ return new AudioOutput(this, parent); ++ + case EffectClass: + return new AudioEffect(this, args[0].toInt(), parent); +-#endif //QT_NO_PHONON_EFFECT ++ + case AudioDataOutputClass: +- logMessage("createObject() : AudioDataOutput not implemented"); +- break; ++ return new AudioDataOutput(this, parent); + +-#ifndef QT_NO_PHONON_VIDEO + case VideoDataOutputClass: + logMessage("createObject() : VideoDataOutput not implemented"); + break; +@@ -141,11 +143,9 @@ QObject *Backend::createObject(BackendIn + QWidget *widget = qobject_cast<QWidget*>(parent); + return new VideoWidget(this, widget); + } +-#endif //QT_NO_PHONON_VIDEO +-#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT ++ + case VolumeFaderEffectClass: + return new VolumeFaderEffect(this, parent); +-#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + + case VisualizationClass: //Fall through + default: +@@ -214,14 +214,14 @@ QStringList Backend::availableMimeTypes( + GstPluginFeature *feature = GST_PLUGIN_FEATURE(iter->data); + QString klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature)); + +- if (klass == QLatin1String("Codec/Decoder") || +- klass == QLatin1String("Codec/Decoder/Audio") || +- klass == QLatin1String("Codec/Decoder/Video") || +- klass == QLatin1String("Codec/Demuxer") || +- klass == QLatin1String("Codec/Demuxer/Audio") || +- klass == QLatin1String("Codec/Demuxer/Video") || +- klass == QLatin1String("Codec/Parser") || +- klass == QLatin1String("Codec/Parser/Audio") || ++ if (klass == QLatin1String("Codec/Decoder") || ++ klass == QLatin1String("Codec/Decoder/Audio") || ++ klass == QLatin1String("Codec/Decoder/Video") || ++ klass == QLatin1String("Codec/Demuxer") || ++ klass == QLatin1String("Codec/Demuxer/Audio") || ++ klass == QLatin1String("Codec/Demuxer/Video") || ++ klass == QLatin1String("Codec/Parser") || ++ klass == QLatin1String("Codec/Parser/Audio") || + klass == QLatin1String("Codec/Parser/Video")) { + + const GList *static_templates; +@@ -258,6 +258,10 @@ QList<int> Backend::objectDescriptionInd + if (!isValid()) + return list; + ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ if (pulse->isActive() && (Phonon::AudioOutputDeviceType == type || Phonon::AudioCaptureDeviceType == type)) ++ return pulse->objectDescriptionIndexes(type); ++ + switch (type) { + case Phonon::AudioOutputDeviceType: { + QList<AudioDevice> deviceList = deviceManager()->audioOutputDevices(); +@@ -291,16 +295,17 @@ QHash<QByteArray, QVariant> Backend::obj + if (!isValid()) + return ret; + ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ if (pulse->isActive() && (Phonon::AudioOutputDeviceType == type || Phonon::AudioCaptureDeviceType == type)) ++ return pulse->objectDescriptionProperties(type, index); ++ + switch (type) { + case Phonon::AudioOutputDeviceType: { +- QList<AudioDevice> audioDevices = deviceManager()->audioOutputDevices(); +- foreach(const AudioDevice &device, audioDevices) { +- if (device.id == index) { +- ret.insert("name", device.gstId); +- ret.insert("description", device.description); +- ret.insert("icon", QLatin1String("audio-card")); +- break; +- } ++ AudioDevice* ad; ++ if ((ad = deviceManager()->audioDevice(index))) { ++ ret.insert("name", ad->gstId); ++ ret.insert("description", ad->description); ++ ret.insert("icon", ad->icon); + } + } + break; +@@ -429,7 +434,7 @@ EffectManager* Backend::effectManager() + + /** + * Returns a debuglevel that is determined by the +- * PHONON_GSTREAMER_DEBUG environment variable. ++ * PHONON_GST_DEBUG environment variable. + * + * Warning - important warnings + * Info - general info +--- a/src/3rdparty/phonon/gstreamer/backend.h ++++ b/src/3rdparty/phonon/gstreamer/backend.h +@@ -86,7 +86,6 @@ private Q_SLOTS: + + private: + static gboolean busCall(GstBus *bus, GstMessage *msg, gpointer data); +- QList<QPointer<AudioOutput> > m_audioOutputs; + + DeviceManager *m_deviceManager; + EffectManager *m_effectManager; +--- a/src/3rdparty/phonon/gstreamer/CMakeLists.txt ++++ b/src/3rdparty/phonon/gstreamer/CMakeLists.txt +@@ -1,4 +1,4 @@ +-# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ++# Copyright (C) 2009 Nokia Corporation. All rights reserved. + # Copyright (C) 2008 Matthias Kretz <kretz@kde.org> + # + # This library is free software: you can redistribute it and/or modify +@@ -19,7 +19,7 @@ include(ConfigureChecks.cmake) + if (BUILD_PHONON_GSTREAMER) + include_directories( + ${CMAKE_CURRENT_BINARY_DIR} +- ${GSTREAMER_INCLUDE_DIR} ++ ${GSTREAMER_INCLUDE_DIR} + ${GLIB2_INCLUDE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${X11_X11_INCLUDE_PATH}) +@@ -34,7 +34,6 @@ if (BUILD_PHONON_GSTREAMER) + + set(phonon_gstreamer_SRCS + audiooutput.cpp +- artssink.cpp + backend.cpp + devicemanager.cpp + effectmanager.cpp +@@ -50,14 +49,20 @@ if (BUILD_PHONON_GSTREAMER) + message.cpp + audioeffect.cpp + abstractrenderer.cpp +- x11renderer.cpp + widgetrenderer.cpp + glrenderer.cpp + volumefadereffect.cpp ++ audiodataoutput.cpp + ) + +- find_package(Alsa) +- macro_ensure_version("0.10.22" ${GSTREAMER_VERSION} GSTREAMER_HAS_NONBLOCKING_ALSASINK) ++ if(NOT WIN32) ++ set(phonon_gstreamer_SRCS ++ ${phonon_gstreamer_SRCS} ++ artssink.cpp ++ x11renderer.cpp) ++ macro_optional_find_package(Alsa) ++ macro_ensure_version("0.10.22" ${GSTREAMER_VERSION} GSTREAMER_HAS_NONBLOCKING_ALSASINK) ++ endif(NOT WIN32) + if(ALSA_FOUND AND NOT GSTREAMER_HAS_NONBLOCKING_ALSASINK) + add_definitions(-DUSE_ALSASINK2) + include_directories(${ALSA_INCLUDES}) +@@ -78,6 +83,9 @@ if (BUILD_PHONON_GSTREAMER) + if(ALSA_FOUND) + target_link_libraries(phonon_gstreamer ${ASOUND_LIBRARY}) + endif(ALSA_FOUND) ++ if(USE_INSTALL_PLUGIN) ++ target_link_libraries(phonon_gstreamer ${GSTREAMER_PLUGIN_PBUTILS_LIBRARIES}) ++ endif(USE_INSTALL_PLUGIN) + + install(TARGETS phonon_gstreamer DESTINATION ${PLUGIN_INSTALL_DIR}/plugins/phonon_backend) + install(FILES gstreamer.desktop DESTINATION ${SERVICES_INSTALL_DIR}/phononbackends) +--- a/src/3rdparty/phonon/gstreamer/ConfigureChecks.cmake ++++ b/src/3rdparty/phonon/gstreamer/ConfigureChecks.cmake +@@ -1,4 +1,4 @@ +-# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ++# Copyright (C) 2009 Nokia Corporation. All rights reserved. + # + # This library is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published by +@@ -17,6 +17,7 @@ macro_log_feature(GSTREAMER_FOUND "GStre + + macro_optional_find_package(GStreamerPlugins) + macro_log_feature(GSTREAMER_PLUGIN_VIDEO_LIBRARIES "GStreamer video plugin" "The gstreamer video plugin (part of gstreamer-plugins-base 0.10) is required for the multimedia gstreamer backend" "http://gstreamer.freedesktop.org/modules/" FALSE "0.10") ++macro_log_feature(GSTREAMER_PLUGIN_AUDIO_LIBRARIES "GStreamer audio plugin" "The gstreamer audio plugin (part of gstreamer-plugins-base 0.10) is required for the multimedia gstreamer backend" "http://gstreamer.freedesktop.org/modules/" FALSE "0.10") + + macro_optional_find_package(GLIB2) + macro_log_feature(GLIB2_FOUND "GLib2" "GLib 2 is required to compile the gstreamer backend for Phonon" "http://www.gtk.org/download/" FALSE) +@@ -30,8 +31,8 @@ macro_log_feature(LIBXML2_FOUND "LibXml2 + macro_optional_find_package(OpenGL) + macro_log_feature(OPENGL_FOUND "OpenGL" "OpenGL support is required to compile the gstreamer backend for Phonon" "" FALSE) + +-if (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) ++if (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GSTREAMER_PLUGIN_AUDIO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) + set(BUILD_PHONON_GSTREAMER TRUE) +-else (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) ++else (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GSTREAMER_PLUGIN_AUDIO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) + set(BUILD_PHONON_GSTREAMER FALSE) +-endif (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) ++endif (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GSTREAMER_PLUGIN_AUDIO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) +--- a/src/3rdparty/phonon/gstreamer/devicemanager.cpp ++++ b/src/3rdparty/phonon/gstreamer/devicemanager.cpp +@@ -24,6 +24,7 @@ + #include "widgetrenderer.h" + #include "x11renderer.h" + #include "artssink.h" ++#include "pulsesupport_p.h" + + #ifdef USE_ALSASINK2 + #include "alsasink2.h" +@@ -44,9 +45,12 @@ namespace Gstreamer + AudioDevice::AudioDevice(DeviceManager *manager, const QByteArray &gstId) + : gstId(gstId) + { +- //get an id +- static int counter = 0; +- id = counter++; ++ // This should never be called when PulseAudio is active. ++ Q_ASSERT(!PulseSupport::getInstance()->isActive()); ++ ++ id = manager->allocateDeviceId(); ++ icon = "audio-card"; ++ + //get name from device + if (gstId == "default") { + description = "Default audio device"; +@@ -71,22 +75,25 @@ AudioDevice::AudioDevice(DeviceManager * + DeviceManager::DeviceManager(Backend *backend) + : QObject(backend) + , m_backend(backend) ++ , m_audioDeviceCounter(0) + { +- m_audioSink = qgetenv("PHONON_GST_AUDIOSINK"); +- m_videoSinkWidget = qgetenv("PHONON_GST_VIDEOMODE"); +- +-#ifndef QT_NO_SETTINGS + QSettings settings(QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ m_audioSink = qgetenv("PHONON_GST_AUDIOSINK"); + if (m_audioSink.isEmpty()) { + m_audioSink = settings.value(QLatin1String("audiosink"), "Auto").toByteArray().toLower(); ++ if (m_audioSink == "auto" && pulse->isActive()) ++ m_audioSink = "pulsesink"; + } ++ if ("pulsesink" != m_audioSink) ++ pulse->disable(); + ++ m_videoSinkWidget = qgetenv("PHONON_GST_VIDEOMODE"); + if (m_videoSinkWidget.isEmpty()) { + m_videoSinkWidget = settings.value(QLatin1String("videomode"), "Auto").toByteArray().toLower(); + } +-#endif //QT_NO_SETTINGS + + if (m_backend->isValid()) + updateDeviceList(); +@@ -246,7 +253,6 @@ GstElement *DeviceManager::createAudioSi + return sink; + } + +-#ifndef QT_NO_PHONON_VIDEO + AbstractRenderer *DeviceManager::createVideoRenderer(VideoWidget *parent) + { + #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES) +@@ -269,11 +275,18 @@ AbstractRenderer *DeviceManager::createV + #endif + return new WidgetRenderer(parent); + } +-#endif //QT_NO_PHONON_VIDEO + +-/* +- * Returns a positive device id or -1 if device +- * does not exist ++/** ++ * Allocate a device id for a new audio device ++ */ ++int DeviceManager::allocateDeviceId() ++{ ++ return m_audioDeviceCounter++; ++} ++ ++ ++/** ++ * Returns a positive device id or -1 if device does not exist + * + * The gstId is typically in the format hw:1,0 + */ +@@ -288,16 +301,30 @@ int DeviceManager::deviceId(const QByteA + } + + /** +- * Get a human-readable description from a device id ++ * Returns a gstId or "default" if device does not exist ++ * ++ * The gstId is typically in the format hw:1,0 + */ +-QByteArray DeviceManager::deviceDescription(int id) const ++const QByteArray DeviceManager::gstId(int deviceId) ++{ ++ if (!PulseSupport::getInstance()->isActive()) { ++ AudioDevice *ad = audioDevice(deviceId); ++ if (ad) ++ return QByteArray(ad->gstId); ++ } ++ return QByteArray("default"); ++} ++ ++/** ++* Get the AudioDevice for a given device id ++*/ ++AudioDevice* DeviceManager::audioDevice(int id) + { + for (int i = 0 ; i < m_audioDeviceList.size() ; ++i) { +- if (m_audioDeviceList[i].id == id) { +- return m_audioDeviceList[i].description; +- } ++ if (m_audioDeviceList[i].id == id) ++ return &m_audioDeviceList[i]; + } +- return QByteArray(); ++ return NULL; + } + + /** +@@ -311,8 +338,11 @@ void DeviceManager::updateDeviceList() + QList<QByteArray> list; + + if (audioSink) { +- list = GstHelper::extractProperties(audioSink, "device"); +- list.prepend("default"); ++ if (!PulseSupport::getInstance()->isActive()) { ++ // If we're using pulse, the PulseSupport class takes care of things for us. ++ list = GstHelper::extractProperties(audioSink, "device"); ++ list.prepend("default"); ++ } + + for (int i = 0 ; i < list.size() ; ++i) { + QByteArray gstId = list.at(i); +--- a/src/3rdparty/phonon/gstreamer/devicemanager.h ++++ b/src/3rdparty/phonon/gstreamer/devicemanager.h +@@ -42,6 +42,7 @@ public : + int id; + QByteArray gstId; + QByteArray description; ++ QString icon; + }; + + class DeviceManager : public QObject { +@@ -51,8 +52,10 @@ public: + virtual ~DeviceManager(); + const QList<AudioDevice> audioOutputDevices() const; + GstPad *requestPad(int device) const; ++ int allocateDeviceId(); + int deviceId(const QByteArray &gstId) const; +- QByteArray deviceDescription(int id) const; ++ const QByteArray gstId(int id); ++ AudioDevice* audioDevice(int id); + GstElement *createGNOMEAudioSink(Category category); + GstElement *createAudioSink(Category category = NoCategory); + AbstractRenderer *createVideoRenderer(VideoWidget *parent); +@@ -68,6 +71,7 @@ private: + bool canOpenDevice(GstElement *element) const; + Backend *m_backend; + QList <AudioDevice> m_audioDeviceList; ++ int m_audioDeviceCounter; + QTimer m_devicePollTimer; + QByteArray m_audioSink; + QByteArray m_videoSinkWidget; +--- a/src/3rdparty/phonon/gstreamer/effect.cpp ++++ b/src/3rdparty/phonon/gstreamer/effect.cpp +@@ -25,8 +25,8 @@ + + #include <gst/gst.h> + +-#ifndef QT_NO_PHONON_EFFECT + QT_BEGIN_NAMESPACE ++ + namespace Phonon + { + namespace Gstreamer +@@ -241,6 +241,6 @@ void Effect::setParameterValue(const Eff + + } + } //namespace Phonon::Gstreamer ++ + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_EFFECT + #include "moc_effect.cpp" +--- a/src/3rdparty/phonon/gstreamer/effect.h ++++ b/src/3rdparty/phonon/gstreamer/effect.h +@@ -28,8 +28,8 @@ + + #include <gst/gst.h> + +-#ifndef QT_NO_PHONON_EFFECT + QT_BEGIN_NAMESPACE ++ + namespace Phonon + { + namespace Gstreamer +@@ -58,7 +58,7 @@ namespace Gstreamer + QList<Phonon::EffectParameter> m_parameterList; + }; + }} //namespace Phonon::Gstreamer ++ + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_EFFECT + + #endif // Phonon_GSTREAMER_EFFECT_H +--- a/src/3rdparty/phonon/gstreamer/gstreamer.desktop ++++ b/src/3rdparty/phonon/gstreamer/gstreamer.desktop +@@ -10,27 +10,69 @@ Icon=phonon-gstreamer + InitialPreference=10 + + Name=GStreamer ++Name[bg]=GStreamer ++Name[ca]=GStreamer ++Name[cs]=GStreamer ++Name[da]=GStreamer ++Name[de]=GStreamer ++Name[el]=GStreamer ++Name[en_GB]=GStreamer ++Name[es]=GStreamer ++Name[et]=GStreamer ++Name[eu]=GStreamer ++Name[fr]=GStreamer ++Name[ga]=GStreamer ++Name[gl]=GStreamer ++Name[hsb]=GStreamer ++Name[hu]=GStreamer ++Name[is]=GStreamer ++Name[it]=GStreamer ++Name[ja]=GStreamer ++Name[ko]=GStreamer ++Name[ku]=GStreamer ++Name[lt]=GStreamer ++Name[lv]=GStreamer ++Name[nds]=GStreamer ++Name[nl]=GStreamer ++Name[nn]=GStreamer + Name[pa]=ਜੀਸਟੀਰਮਰ ++Name[pl]=GStreamer ++Name[pt]=GStreamer ++Name[pt_BR]=GStreamer ++Name[se]=GStreamer ++Name[sk]=GStreamer ++Name[sl]=GStreamer + Name[sr]=Гстример ++Name[sr@latin]=GStreamer + Name[sv]=Gstreamer ++Name[tr]=GStreamer ++Name[uk]=GStreamer + Name[x-test]=xxGStreamerxx ++Name[zh_CN]=GStreamer ++Name[zh_TW]=GStreamer + + Comment=Phonon GStreamer backend + Comment[bg]=Phonon GStreamer + Comment[ca]=Dorsal GStreamer del Phonon ++Comment[cs]=Phonon GStreamer backend + Comment[da]=GStreamer-backend til Phonon + Comment[de]=Phonon-Treiber für GStreamer + Comment[el]=Σύστημα υποστήριξης GStreamer του Phonon ++Comment[en_GB]=Phonon GStreamer backend + Comment[es]=Motor GStreamer para Phonon + Comment[et]=Phononi GStreameri taustaprogramm ++Comment[eu]=Phonon GStreamer backend + Comment[fr]=Système de gestion GStreamer pour Phonon + Comment[ga]=Inneall GStreamer le haghaidh Phonon + Comment[gl]=Infraestrutura de GStreamer para Phonon ++Comment[hsb]=Phonon GStreamer backend ++Comment[hu]=Phonon GStreamer modul + Comment[is]=Phonon GStreamer bakendi + Comment[it]=Motore Gstreamer di Phonon + Comment[ja]=Phonon GStreamer バックエンド + Comment[ko]=Phonon GStreamer 백엔드 + Comment[ku]=Binesaza Phonon GStreamer ++Comment[lt]=Phonon GStreamer galinė sąsaja + Comment[lv]=Phonon GStreamer aizmugure + Comment[nds]=Phonon-Hülpprogramm GStreamer + Comment[nl]=GStreamer-backend (Phonon) +@@ -39,6 +81,7 @@ Comment[pa]=ਫੋਨੋਨ ਜਸਟੀ + Comment[pl]=Obsługa GStreamera przez Phonon + Comment[pt]=Infra-estrutura do GStreamer para o Phonon + Comment[pt_BR]=Infraestrutura Phonon GStreamer ++Comment[se]=Phonon GStreamer duogášmohtor + Comment[sk]=GStreamer podsystém + Comment[sl]=Phononova hrbtenica GStreamer + Comment[sr]=Гстример као позадина Фонона +--- a/src/3rdparty/phonon/gstreamer/mediaobject.cpp ++++ b/src/3rdparty/phonon/gstreamer/mediaobject.cpp +@@ -16,6 +16,7 @@ + */ + #include <cmath> + #include <gst/interfaces/propertyprobe.h> ++#include <gst/pbutils/install-plugins.h> + #include "common.h" + #include "mediaobject.h" + #include "videowidget.h" +@@ -23,6 +24,7 @@ + #include "backend.h" + #include "streamreader.h" + #include "phononsrc.h" ++#include "phonon-config-gstreamer.h" + #include <QtCore> + #include <QtCore/QTimer> + #include <QtCore/QVector> +@@ -53,6 +55,7 @@ MediaObject::MediaObject(Backend *backen + , m_tickTimer(new QTimer(this)) + , m_prefinishMark(0) + , m_transitionTime(0) ++ , m_isStream(false) + , m_posAtSeek(-1) + , m_prefinishMarkReachedNotEmitted(true) + , m_aboutToFinishEmitted(false) +@@ -79,6 +82,7 @@ MediaObject::MediaObject(Backend *backen + , m_autoplayTitles(true) + , m_availableTitles(0) + , m_currentTitle(1) ++ , m_pendingTitle(1) + { + qRegisterMetaType<GstCaps*>("GstCaps*"); + qRegisterMetaType<State>("State"); +@@ -87,7 +91,7 @@ MediaObject::MediaObject(Backend *backen + m_name = "MediaObject" + QString::number(count++); + + if (!m_backend->isValid()) { +- setError(tr("Cannot start playback. \n\nCheck your GStreamer installation and make sure you " ++ setError(tr("Cannot start playback. \n\nCheck your Gstreamer installation and make sure you " + "\nhave libgstreamer-plugins-base installed."), Phonon::FatalError); + } else { + m_root = this; +@@ -95,8 +99,8 @@ MediaObject::MediaObject(Backend *backen + m_backend->addBusWatcher(this); + connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick())); + } +- connect(this, SIGNAL(stateChanged(Phonon::State,Phonon::State)), +- this, SLOT(notifyStateChange(Phonon::State,Phonon::State))); ++ connect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)), ++ this, SLOT(notifyStateChange(Phonon::State, Phonon::State))); + + } + +@@ -136,6 +140,14 @@ QString stateString(const Phonon::State + return QString(); + } + ++void ++pluginInstallationDone( GstInstallPluginsReturn res, gpointer userData ) ++{ ++ // Nothing inside yet ++ Q_UNUSED(res); ++ Q_UNUSED(userData); ++} ++ + void MediaObject::saveState() + { + //Only first resumeState is respected +@@ -195,13 +207,35 @@ void MediaObject::noMorePadsAvailable () + if (m_missingCodecs.size() > 0) { + bool canPlay = (m_hasAudio || m_videoStreamFound); + Phonon::ErrorType error = canPlay ? Phonon::NormalError : Phonon::FatalError; ++#ifdef PLUGIN_INSTALL_API ++ GstInstallPluginsContext *ctx = gst_install_plugins_context_new (); ++ gchar *details[2]; ++ details[0] = m_missingCodecs[0].toLocal8Bit().data(); ++ details[1] = NULL; ++ GstInstallPluginsReturn status; ++ ++ status = gst_install_plugins_async( details, ctx, pluginInstallationDone, NULL ); ++ gst_install_plugins_context_free ( ctx ); ++ ++ if ( status != GST_INSTALL_PLUGINS_STARTED_OK ) ++ { ++ if( status == GST_INSTALL_PLUGINS_HELPER_MISSING ) ++ setError(QString(tr("Missing codec helper script assistant.")), Phonon::FatalError ); ++ else ++ setError(QString(tr("Plugin codec installation failed for codec: %0")) ++ .arg(m_missingCodecs[0].split("|")[3]), error); ++ } ++ m_missingCodecs.clear(); ++#else ++ QString codecs = m_missingCodecs.join(", "); ++ + if (error == Phonon::NormalError && m_hasVideo && !m_videoStreamFound) { + m_hasVideo = false; + emit hasVideoChanged(false); + } +- QString codecs = m_missingCodecs.join(", "); + setError(QString(tr("A required codec is missing. You need to install the following codec(s) to play this content: %0")).arg(codecs), error); + m_missingCodecs.clear(); ++#endif + } + } + +@@ -226,7 +260,6 @@ void MediaObject::cb_unknown_type (GstEl + QString value = "unknown codec"; + + // These functions require GStreamer > 0.10.12 +-#ifndef QT_NO_LIBRARY + static Ptr_gst_pb_utils_init p_gst_pb_utils_init = 0; + static Ptr_gst_pb_utils_get_codec_description p_gst_pb_utils_get_codec_description = 0; + if (!p_gst_pb_utils_init) { +@@ -240,15 +273,21 @@ void MediaObject::cb_unknown_type (GstEl + codecName = p_gst_pb_utils_get_codec_description (caps); + value = QString::fromUtf8(codecName); + g_free (codecName); +- } else +-#endif //QT_NO_LIBRARY +- { ++ } else { + // For GStreamer versions < 0.10.12 + GstStructure *str = gst_caps_get_structure (caps, 0); + value = QString::fromUtf8(gst_structure_get_name (str)); +- + } +- media->addMissingCodecName(value); ++ ++#ifdef PLUGIN_INSTALL_API ++ QString plugins = QString("gstreamer|0.10|%0|%1|decoder-%2") ++ .arg( qApp->applicationName() ) ++ .arg( value ) ++ .arg( QString::fromUtf8(gst_caps_to_string (caps) ) ); ++ media->addMissingCodecName( plugins ); ++#else ++ media->addMissingCodecName( value ); ++#endif + } + + static void notifyVideoCaps(GObject *obj, GParamSpec *, gpointer data) +@@ -309,7 +348,7 @@ void MediaObject::connectVideo(GstPad *p + m_backend->logMessage("Video track connected", Backend::Info, this); + // Note that the notify::caps _must_ be installed after linking to work with Dapper + m_capsHandler = g_signal_connect(pad, "notify::caps", G_CALLBACK(notifyVideoCaps), this); +- ++ + if (!m_loading && !m_hasVideo) { + m_hasVideo = m_videoStreamFound; + emit hasVideoChanged(m_hasVideo); +@@ -368,7 +407,10 @@ bool MediaObject::createPipefromURL(cons + } + + // Create a new datasource based on the input URL +- QByteArray encoded_cstr_url = url.toEncoded(); ++ // add the 'file' scheme if it's missing; the double '/' is needed! ++ QByteArray encoded_cstr_url = (url.scheme() == QLatin1String("") ? ++ "file://" + url.toEncoded() : ++ url.toEncoded()); + m_datasource = gst_element_make_from_uri(GST_URI_SRC, encoded_cstr_url.constData(), (const char*)NULL); + if (!m_datasource) + return false; +@@ -388,6 +430,14 @@ bool MediaObject::createPipefromURL(cons + g_object_set (G_OBJECT (m_datasource), "read-speed", 2, (const char*)NULL); + m_backend->logMessage(QString("new device speed : 2X"), Backend::Info, this); + } ++ } ++ ++ /* make HTTP sources send extra headers so we get icecast ++ * metadata in case the stream is an icecast stream */ ++ if (encoded_cstr_url.startsWith("http://") ++ && g_object_class_find_property (G_OBJECT_GET_CLASS (m_datasource), "iradio-mode")) { ++ g_object_set (m_datasource, "iradio-mode", TRUE, NULL); ++ m_isStream = true; + } + + // Link data source into pipeline +@@ -408,7 +458,6 @@ bool MediaObject::createPipefromURL(cons + */ + bool MediaObject::createPipefromStream(const MediaSource &source) + { +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + // Remove any existing data source + if (m_datasource) { + gst_bin_remove(GST_BIN(m_pipeline), m_datasource); +@@ -430,10 +479,6 @@ bool MediaObject::createPipefromStream(c + return false; + } + return true; +-#else //QT_NO_PHONON_ABSTRACTMEDIASTREAM +- Q_UNUSED(source); +- return false; +-#endif + } + + void MediaObject::createPipeline() +@@ -676,7 +721,7 @@ void MediaObject::changeState(State news + return; + + Phonon::State oldState = m_state; +- m_state = newstate; // m_state must be set before emitting, since ++ m_state = newstate; // m_state must be set before emitting, since + // Error state requires that state() will return the new value + m_pendingState = newstate; + emit stateChanged(newstate, oldState); +@@ -861,7 +906,7 @@ void MediaObject::setSource(const MediaS + // such as failing duration queries etc + GstState state; + gst_element_set_state(m_pipeline, GST_STATE_NULL); +- gst_element_get_state (m_pipeline, &state, NULL, 2000); ++ gst_element_get_state(m_pipeline, &state, NULL, 2000); + + m_source = source; + emit currentSourceChanged(m_source); +@@ -885,7 +930,7 @@ void MediaObject::setSource(const MediaS + m_aboutToFinishEmitted = false; + m_error = NoError; + m_errorString = QString(); +- ++ + m_bufferPercent = 0; + m_prefinishMarkReachedNotEmitted = true; + m_aboutToFinishEmitted = false; +@@ -894,11 +939,16 @@ void MediaObject::setSource(const MediaS + setTotalTime(-1); + m_atEndOfStream = false; + +- // Clear exising meta tags ++ m_availableTitles = 0; ++ m_pendingTitle = 1; ++ m_currentTitle = 1; ++ ++ // Clear existing meta tags + m_metaData.clear(); ++ m_isStream = false; + + switch (source.type()) { +- case MediaSource::Url: { ++ case MediaSource::Url: { + if (createPipefromURL(source.url())) + m_loading = true; + else +@@ -930,9 +980,9 @@ void MediaObject::setSource(const MediaS + + case MediaSource::Disc: + { +- QString mediaUrl; +- switch (source.discType()) { +- case Phonon::NoDisc: ++ QString mediaUrl; ++ switch (source.discType()) { ++ case Phonon::NoDisc: + qWarning() << "I should never get to see a MediaSource that is a disc but doesn't specify which one"; + return; + case Phonon::Cd: // CD tracks can be specified by setting the url in the following way uri=cdda:4 +@@ -1004,13 +1054,10 @@ void MediaObject::getStreamInfo() + emit hasVideoChanged(m_hasVideo); + } + +- m_availableTitles = 1; +- gint64 titleCount; +- GstFormat format = gst_format_get_by_nick("track"); +- if (gst_element_query_duration (m_pipeline, &format, &titleCount)) { +- //check if returned format is still "track", +- //gstreamer sometimes returns the total time, if tracks information is not available. +- if (qstrcmp(gst_format_get_name(format), "track") == 0) { ++ if (m_source.discType() == Phonon::Cd) { ++ gint64 titleCount; ++ GstFormat format = gst_format_get_by_nick("track"); ++ if (gst_element_query_duration (m_pipeline, &format, &titleCount)) { + int oldAvailableTitles = m_availableTitles; + m_availableTitles = (int)titleCount; + if (m_availableTitles != oldAvailableTitles) { +@@ -1077,7 +1124,7 @@ void MediaObject::seek(qint64 time) + } + + quint64 current = currentTime(); +- quint64 total = totalTime(); ++ quint64 total = totalTime(); + + if (current < total - m_prefinishMark) + m_prefinishMarkReachedNotEmitted = true; +@@ -1098,7 +1145,7 @@ void MediaObject::emitTick() + + if (m_tickInterval > 0 && currentTime != m_previousTickTime) { + emit tick(currentTime); +- m_previousTickTime = currentTime; ++ m_previousTickTime = currentTime; + } + if (m_state == Phonon::PlayingState) { + if (currentTime >= totalTime - m_prefinishMark) { +@@ -1109,7 +1156,12 @@ void MediaObject::emitTick() + } + // Prepare load of next source + if (currentTime >= totalTime - ABOUT_TO_FINNISH_TIME) { +- if (!m_aboutToFinishEmitted) { ++ if (m_source.type() == MediaSource::Disc && ++ m_autoplayTitles && ++ m_availableTitles > 1 && ++ m_currentTitle < m_availableTitles) { ++ m_aboutToFinishEmitted = false; ++ } else if (!m_aboutToFinishEmitted) { + m_aboutToFinishEmitted = true; // track is about to finish + emit aboutToFinish(); + } +@@ -1213,7 +1265,7 @@ void MediaObject::handleBusMessage(const + + switch (GST_MESSAGE_TYPE (gstMessage)) { + +- case GST_MESSAGE_EOS: ++ case GST_MESSAGE_EOS: + m_backend->logMessage("EOS recieved", Backend::Info, this); + handleEndOfStream(); + break; +@@ -1222,14 +1274,98 @@ void MediaObject::handleBusMessage(const + GstTagList* tag_list = 0; + gst_message_parse_tag(gstMessage, &tag_list); + if (tag_list) { ++ TagMap newTags; ++ gst_tag_list_foreach (tag_list, &foreach_tag_function, &newTags); ++ gst_tag_list_free(tag_list); ++ ++ // Determine if we should no fake the album/artist tags. ++ // This is a little confusing as we want to fake it on initial ++ // connection where title, album and artist are all missing. ++ // There are however times when we get just other information, ++ // e.g. codec, and so we want to only do clever stuff if we ++ // have a commonly available tag (ORGANIZATION) or we have a ++ // change in title ++ bool fake_it = ++ (m_isStream ++ && ((!newTags.contains("TITLE") ++ && newTags.contains("ORGANIZATION")) ++ || (newTags.contains("TITLE") ++ && m_metaData.value("TITLE") != newTags.value("TITLE"))) ++ && !newTags.contains("ALBUM") ++ && !newTags.contains("ARTIST")); ++ + TagMap oldMap = m_metaData; // Keep a copy of the old one for reference +- // Append any new meta tags to the existing tag list +- gst_tag_list_foreach (tag_list, &foreach_tag_function, &m_metaData); ++ ++ // Now we've checked the new data, append any new meta tags to the existing tag list ++ // We cannot use TagMap::iterator as this is a multimap and when streaming data ++ // could in theory be lost. ++ QList<QString> keys = newTags.keys(); ++ for (QList<QString>::iterator i = keys.begin(); i != keys.end(); ++i) { ++ QString key = *i; ++ if (m_isStream) { ++ // If we're streaming, we need to remove data in m_metaData ++ // in order to stop it filling up indefinitely (as it's a multimap) ++ m_metaData.remove(key); ++ } ++ QList<QString> values = newTags.values(key); ++ for (QList<QString>::iterator j = values.begin(); j != values.end(); ++j) { ++ QString value = *j; ++ QString currVal = m_metaData.value(key); ++ if (!m_metaData.contains(key) || currVal != value) { ++ m_metaData.insert(key, value); ++ } ++ } ++ } ++ + m_backend->logMessage("Meta tags found", Backend::Info, this); +- if (oldMap != m_metaData && !m_loading) +- emit metaDataChanged(m_metaData); +- gst_tag_list_free(tag_list); +- } ++ if (oldMap != m_metaData) { ++ // This is a bit of a hack to ensure that stream metadata is ++ // returned. We get as much as we can from the Shoutcast server's ++ // StreamTitle= header. If further info is decoded from the stream ++ // itself later, then it will overwrite this info. ++ if (m_isStream && fake_it) { ++ m_metaData.remove("ALBUM"); ++ m_metaData.remove("ARTIST"); ++ ++ // Detect whether we want to "fill in the blanks" ++ QString str; ++ if (m_metaData.contains("TITLE")) ++ { ++ str = m_metaData.value("TITLE"); ++ int splitpoint; ++ // Check to see if our title matches "%s - %s" ++ // Where neither %s are empty... ++ if ((splitpoint = str.indexOf(" - ")) > 0 ++ && str.size() > (splitpoint+3)) { ++ m_metaData.insert("ARTIST", str.left(splitpoint)); ++ m_metaData.replace("TITLE", str.mid(splitpoint+3)); ++ } ++ } else { ++ str = m_metaData.value("GENRE"); ++ if (!str.isEmpty()) ++ m_metaData.insert("TITLE", str); ++ else ++ m_metaData.insert("TITLE", "Streaming Data"); ++ } ++ if (!m_metaData.contains("ARTIST")) { ++ str = m_metaData.value("LOCATION"); ++ if (!str.isEmpty()) ++ m_metaData.insert("ARTIST", str); ++ else ++ m_metaData.insert("ARTIST", "Streaming Data"); ++ } ++ str = m_metaData.value("ORGANIZATION"); ++ if (!str.isEmpty()) ++ m_metaData.insert("ALBUM", str); ++ else ++ m_metaData.insert("ALBUM", "Streaming Data"); ++ } ++ // As we manipulate the title, we need to recompare ++ // oldMap and m_metaData here... ++ if (oldMap != m_metaData && !m_loading) ++ emit metaDataChanged(m_metaData); ++ } ++ } + } + break; + +@@ -1255,6 +1391,9 @@ void MediaObject::handleBusMessage(const + m_backend->logMessage("gstreamer: pipeline state set to playing", Backend::Info, this); + m_tickTimer->start(); + changeState(Phonon::PlayingState); ++ if ((m_source.type() == MediaSource::Disc) && (m_currentTitle != m_pendingTitle)) { ++ setTrack(m_pendingTitle); ++ } + if (m_resumeState && m_oldState == Phonon::PlayingState) { + seek(m_oldPos); + m_resumeState = false; +@@ -1290,6 +1429,9 @@ void MediaObject::handleBusMessage(const + changeState(Phonon::StoppedState); + m_backend->logMessage("gstreamer: pipeline state set to ready", Backend::Debug, this); + m_tickTimer->stop(); ++ if ((m_source.type() == MediaSource::Disc) && (m_currentTitle != m_pendingTitle)) { ++ setTrack(m_pendingTitle); ++ } + break; + + case GST_STATE_VOID_PENDING : +@@ -1328,7 +1470,7 @@ void MediaObject::handleBusMessage(const + setError(err->message, Phonon::FatalError); + gst_caps_unref (caps); + gst_object_unref (sinkPad); +- } ++ } + } else { + setError(QString(err->message), Phonon::FatalError); + } +@@ -1400,8 +1542,8 @@ void MediaObject::handleBusMessage(const + //case GST_MESSAGE_STEP_DONE: + //case GST_MESSAGE_LATENCY: only from 0.10.12 + //case GST_MESSAGE_ASYNC_DONE: only from 0.10.13 +- default: +- break; ++ default: ++ break; + } + } + +@@ -1417,7 +1559,8 @@ void MediaObject::handleEndOfStream() + if (!m_seekable) + m_atEndOfStream = true; + +- if (m_autoplayTitles && ++ if (m_source.type() == MediaSource::Disc && ++ m_autoplayTitles && + m_availableTitles > 1 && + m_currentTitle < m_availableTitles) { + _iface_setCurrentTitle(m_currentTitle + 1); +@@ -1502,15 +1645,30 @@ int MediaObject::_iface_currentTitle() c + + void MediaObject::_iface_setCurrentTitle(int title) + { +- GstFormat trackFormat = gst_format_get_by_nick("track"); + m_backend->logMessage(QString("setCurrentTitle %0").arg(title), Backend::Info, this); +- if ((title == m_currentTitle) || (title < 1) || (title > m_availableTitles)) ++ if ((title == m_currentTitle) || (title == m_pendingTitle)) ++ return; ++ ++ m_pendingTitle = title; ++ ++ if (m_state == Phonon::PlayingState || m_state == Phonon::StoppedState) { ++ setTrack(m_pendingTitle); ++ } else { ++ setState(Phonon::StoppedState); ++ } ++} ++ ++void MediaObject::setTrack(int title) ++{ ++ if (((m_state != Phonon::PlayingState) && (m_state != Phonon::StoppedState)) || (title < 1) || (title > m_availableTitles)) + return; + +- m_currentTitle = title; + + //let's seek to the beginning of the song +- if (gst_element_seek_simple(m_pipeline, trackFormat, GST_SEEK_FLAG_FLUSH, m_currentTitle - 1)) { ++ GstFormat trackFormat = gst_format_get_by_nick("track"); ++ m_backend->logMessage(QString("setTrack %0").arg(title), Backend::Info, this); ++ if (gst_element_seek_simple(m_pipeline, trackFormat, GST_SEEK_FLAG_FLUSH, title - 1)) { ++ m_currentTitle = title; + updateTotalTime(); + m_atEndOfStream = false; + emit titleChanged(title); +--- a/src/3rdparty/phonon/gstreamer/mediaobject.h ++++ b/src/3rdparty/phonon/gstreamer/mediaobject.h +@@ -55,6 +55,7 @@ class MediaObject : public QObject, publ + , public MediaNode + { + friend class Stream; ++ friend class AudioDataOutput; + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface + #ifndef QT_NO_PHONON_MEDIACONTROLLER +@@ -236,6 +237,7 @@ private: + int _iface_availableTitles() const; + int _iface_currentTitle() const; + void _iface_setCurrentTitle(int title); ++ void setTrack(int title); + + bool m_resumeState; + State m_oldState; +@@ -250,6 +252,7 @@ private: + MediaSource m_nextSource; + qint32 m_prefinishMark; + qint32 m_transitionTime; ++ bool m_isStream; + + qint64 m_posAtSeek; + +@@ -285,6 +288,7 @@ private: + bool m_autoplayTitles; + int m_availableTitles; + int m_currentTitle; ++ int m_pendingTitle; + }; + } + } //namespace Phonon::Gstreamer +--- /dev/null ++++ b/gstreamer/phonon-config-gstreamer.h.cmake +@@ -0,0 +1,5 @@ ++/* GStreamer install plugin definitions */ ++ ++/* If api-plugin is defined */ ++#cmakedefine PLUGIN_INSTALL_API 1 ++ +--- a/src/3rdparty/phonon/gstreamer/phononsrc.cpp ++++ b/src/3rdparty/phonon/gstreamer/phononsrc.cpp +@@ -109,25 +109,18 @@ static void phonon_src_class_init (Phono + static void phonon_src_init (PhononSrc * src, PhononSrcClass * g_class) + { + Q_UNUSED(g_class); +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + src->device = 0; +-#else +- Q_UNUSED(src); +-#endif + } + + static void phonon_src_finalize (GObject * object) + { +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + PhononSrc *src; + src = GST_PHONON_SRC (object); + delete src->device; + src->device = 0; +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + G_OBJECT_CLASS (parent_class)->finalize (object); + } + +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + static gboolean phonon_src_set_device(PhononSrc * src, StreamReader* device) + { + GstState state; +@@ -152,7 +145,6 @@ wrong_state: + return FALSE; + } + } +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + + static void phonon_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) + { +@@ -161,7 +153,6 @@ static void phonon_src_set_property (GOb + src = GST_PHONON_SRC (object); + + switch (prop_id) { +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + case ARG_PHONONSRC: + { + StreamReader *dev = (StreamReader*)(g_value_get_pointer(value)); +@@ -169,9 +160,6 @@ static void phonon_src_set_property (GOb + phonon_src_set_device(src, dev); + break; + } +-#else +- Q_UNUSED(value); +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -186,13 +174,9 @@ static void phonon_src_get_property (GOb + src = GST_PHONON_SRC (object); + + switch (prop_id) { +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + case ARG_PHONONSRC: + g_value_set_pointer(value, src->device); + break; +-#else //QT_NO_PHONON_ABSTRACTMEDIASTREAM +- Q_UNUSED(value); +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -201,7 +185,6 @@ static void phonon_src_get_property (GOb + + static GstFlowReturn phonon_src_create_read (PhononSrc * src, guint64 offset, guint length, GstBuffer ** buffer) + { +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + Q_ASSERT(src->device); + if (!src->device) + return GST_FLOW_ERROR; +@@ -221,13 +204,6 @@ static GstFlowReturn phonon_src_create_r + + gst_mini_object_unref(GST_MINI_OBJECT(buf)); + return GST_FLOW_ERROR; +-#else //QT_NO_PHONON_ABSTRACTMEDIASTREAM +- Q_UNUSED(src); +- Q_UNUSED(offset); +- Q_UNUSED(length); +- Q_UNUSED(buffer); +- return GST_FLOW_ERROR; +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + } + + static GstFlowReturn phonon_src_create (GstBaseSrc * basesrc, guint64 offset, guint length, GstBuffer ** buffer) +@@ -242,23 +218,19 @@ static GstFlowReturn phonon_src_create ( + static gboolean phonon_src_is_seekable (GstBaseSrc * basesrc) + { + PhononSrc *src = GST_PHONON_SRC (basesrc); +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (src->device) + return src->device->streamSeekable(); +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + return false; + } + + static gboolean phonon_src_get_size (GstBaseSrc * basesrc, guint64 * size) + { +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + PhononSrc *src; + src = GST_PHONON_SRC (basesrc); + if (src->device && src->device->streamSeekable()) { + *size = src->device->streamSize(); + return TRUE; + } +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + *size = 0; + return FALSE; + } +--- a/src/3rdparty/phonon/gstreamer/phononsrc.h ++++ b/src/3rdparty/phonon/gstreamer/phononsrc.h +@@ -49,9 +49,7 @@ typedef struct _PhononSrcClass PhononSrc + // PhononSrc: + struct _PhononSrc { + GstBaseSrc element; +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + StreamReader *device; +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + }; + + struct _PhononSrcClass { +--- a/src/3rdparty/phonon/gstreamer/qwidgetvideosink.h ++++ b/src/3rdparty/phonon/gstreamer/qwidgetvideosink.h +@@ -19,6 +19,7 @@ + #define Phonon_GSTREAMER_VIDEOSINK_H + + #include "common.h" ++#include "qwidgetvideosink.h" + + #include <QtCore/QByteArray> + #include <QtCore/QEvent> +--- a/src/3rdparty/phonon/gstreamer/streamreader.cpp ++++ b/src/3rdparty/phonon/gstreamer/streamreader.cpp +@@ -20,7 +20,7 @@ along with this library. If not, see <h + #include <phonon/streaminterface.h> + + QT_BEGIN_NAMESPACE +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM ++ + namespace Phonon + { + namespace Gstreamer +@@ -49,6 +49,5 @@ bool StreamReader::read(quint64 pos, int + + } + } +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + + QT_END_NAMESPACE +--- a/src/3rdparty/phonon/gstreamer/streamreader.h ++++ b/src/3rdparty/phonon/gstreamer/streamreader.h +@@ -23,8 +23,6 @@ along with this library. If not, see <h + + QT_BEGIN_NAMESPACE + +-#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM +- + namespace Phonon + { + class MediaSource; +@@ -93,8 +91,6 @@ private: + } + } + +-#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM +- + QT_END_NAMESPACE + + #endif +--- a/src/3rdparty/phonon/gstreamer/videowidget.cpp ++++ b/src/3rdparty/phonon/gstreamer/videowidget.cpp +@@ -33,7 +33,6 @@ + #include "widgetrenderer.h" + #include "x11renderer.h" + +-#ifndef QT_NO_PHONON_VIDEO + QT_BEGIN_NAMESPACE + + namespace Phonon +@@ -384,6 +383,5 @@ void VideoWidget::mediaNodeEvent(const M + } //namespace Phonon::Gstreamer + + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_VIDEO + + #include "moc_videowidget.cpp" +--- a/src/3rdparty/phonon/gstreamer/videowidget.h ++++ b/src/3rdparty/phonon/gstreamer/videowidget.h +@@ -25,10 +25,10 @@ + #include "common.h" + #include "medianode.h" + #include "abstractrenderer.h" ++#include "videowidget.h" + + #include <gst/gst.h> + +-#ifndef QT_NO_PHONON_VIDEO + QT_BEGIN_NAMESPACE + + class QString; +@@ -103,5 +103,5 @@ private: + } //namespace Phonon::Gstreamer + + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_VIDEO ++ + #endif // Phonon_GSTREAMER_VIDEOWIDGET_H +--- a/src/3rdparty/phonon/gstreamer/volumefadereffect.cpp ++++ b/src/3rdparty/phonon/gstreamer/volumefadereffect.cpp +@@ -21,11 +21,11 @@ + + QT_BEGIN_NAMESPACE + +-#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + namespace Phonon + { + namespace Gstreamer + { ++ + VolumeFaderEffect::VolumeFaderEffect(Backend *backend, QObject *parent) + : Effect(backend, parent, AudioSource | AudioSink) + , m_fadeCurve(Phonon::VolumeFaderEffect::Fade3Decibel) +@@ -156,7 +156,7 @@ bool VolumeFaderEffect::event(QEvent *ev + } + + }} //namespace Phonon::Gstreamer +-#endif //QT_NO_PHONON_VOLUMEFADEREFFECT ++ + QT_END_NAMESPACE + + #include "moc_volumefadereffect.cpp" +--- a/src/3rdparty/phonon/gstreamer/volumefadereffect.h ++++ b/src/3rdparty/phonon/gstreamer/volumefadereffect.h +@@ -30,7 +30,7 @@ + #include <gst/gst.h> + + QT_BEGIN_NAMESPACE +-#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT ++ + namespace Phonon + { + namespace Gstreamer +@@ -64,7 +64,7 @@ namespace Gstreamer + QTime m_fadeStartTime; + }; + }} //namespace Phonon::Gstreamer +-#endif //QT_NO_PHONON_VOLUMEFADEREFFECT ++ + QT_END_NAMESPACE + + #endif // Phonon_GSTREAMER_VOLUMEFADEREFFECT_H +--- a/src/3rdparty/phonon/gstreamer/widgetrenderer.cpp ++++ b/src/3rdparty/phonon/gstreamer/widgetrenderer.cpp +@@ -32,7 +32,6 @@ + # define GL_TEXTURE2 0x84C2 + #endif + +-#ifndef QT_NO_PHONON_VIDEO + QT_BEGIN_NAMESPACE + + static void frameRendered() +@@ -149,4 +148,3 @@ bool WidgetRenderer::eventFilter(QEvent + } //namespace Phonon::Gstreamer + + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_VIDEO +--- a/src/3rdparty/phonon/gstreamer/widgetrenderer.h ++++ b/src/3rdparty/phonon/gstreamer/widgetrenderer.h +@@ -26,7 +26,6 @@ + #include <QtOpenGL/QGLWidget> + #endif + +-#ifndef QT_NO_PHONON_VIDEO + QT_BEGIN_NAMESPACE + + class QString; +@@ -60,5 +59,5 @@ private: + } //namespace Phonon::Gstreamer + + QT_END_NAMESPACE +-#endif //QT_NO_PHONON_VIDEO ++ + #endif // Phonon_GSTREAMER_WIDGETRENDERER_H +--- a/src/3rdparty/phonon/gstreamer/x11renderer.cpp ++++ b/src/3rdparty/phonon/gstreamer/x11renderer.cpp +@@ -138,6 +138,7 @@ void X11Renderer::scaleModeChanged(Phono + void X11Renderer::movieSizeChanged(const QSize &movieSize) + { + Q_UNUSED(movieSize); ++ + if (m_renderWidget) { + m_renderWidget->setGeometry(m_videoWidget->calculateDrawFrameRect()); + } +--- /dev/null ++++ b/Mainpage.dox +@@ -0,0 +1,64 @@ ++/** @mainpage The Phonon Library API Reference ++ ++<p><b> ++Overview | ++@ref development ++</b></p> ++ ++<a href="http://phonon.kde.org/">Phonon</a> provides a Qt-style interface for audio ++and video. It is aimed at the average desktop application, whether that is a game ++that needs sound effects or a full-blown multimedia playback application like ++<a href="http://amarok.kde.org/">Amarok</a> or ++<a href="http://www.dragonplayer.org/">Dragon Player</a>. ++ ++If you writing a Qt program and want audio or video playback, then Phonon is almost ++certainly the right choice for you. It provides an API that any Qt developer will ++be comfortable with, and uses the native sound systems on Windows and MacOS. On ++UNIX systems, where there is no one standard multimedia system, it offers ++<a href="http://www.gstreamer.net/">GStreamer</a> and <a href="http://xinehq.de/">Xine</a> ++backends, and more (such as <a href="http://www.videolan.org/">VLC</a>) are available ++from third parties. ++ ++However, Phonon is not aimed at professional audio applications (in the style of ++<a href="http://audacity.sourceforge.net/">Audacity</a>, say). For these applications, ++you will probably want more control over the audio layer than Phonon will give you, ++and you should probably be using something like <a href="http://jackaudio.org/">JACK</a>. ++ ++@authors ++Matthias Kretz \<kretz@kde.org\><br> ++Ricardo Villalba \<rvm@escomposlinux.org\><br> ++Ian Monroe \<ian@monroe.nu\><br> ++Nokia Corporation and/or its subsidiary(-ies) ++ ++@maintainers ++Matthias Kretz \<kretz@kde.org\> ++ ++@licenses ++Libraries: @lgpl<br> ++Examples: @mit<br> ++Backends: @lgpl or @gpl ++ ++*/ ++ ++/** @page development Development ++ ++<p><b> ++@ref index "Overview" | ++Development ++</b></p> ++ ++If you want to get involved with Phonon backend development please subscribe to <a href="https://mail.kde.org/mailman/listinfo/phonon-backends">phonon-backends@kde.org</a>. ++ ++If you want to contribute, make comments or useful suggestions you can also write to the <a href="mailto:kde-multimedia@kde.org">KDE Multimedia mailinglist</a> or <a href="mailto:kretz@kde.org">Matthias Kretz</a>. You can join the mailinglist using the <a href="https://mail.kde.org/mailman/listinfo/kde-multimedia">webinterface</a> or take a look at the <a href="http://lists.kde.org/?l=kde-multimedia">archives</a> ++ ++*/ ++ ++// DOXYGEN_VERSION=4.3 ++// DOXYGEN_NAME=Phonon ++// DOXYGEN_ENABLE=YES ++// DOXYGEN_SET_INPUT = @topdir@/phonon/phonon @topdir@/phonon/Mainpage.dox ++// DOXYGEN_SET_FILE_PATTERNS = *.h *.dox */phononnamespace.h.in ++// ignore backend docs ++// DOXYGEN_SET_EXCLUDE_PATTERNS = *interface.h */phonon/examples/* */tests/* *_p.h */experimental/videocapturedevice/* */phonon/backend* ++ ++// vim:ts=4:sw=4:expandtab:filetype=doxygen +--- a/src/3rdparty/phonon/phonon/abstractmediastream.cpp ++++ b/src/3rdparty/phonon/phonon/abstractmediastream.cpp +@@ -49,6 +49,7 @@ AbstractMediaStream::AbstractMediaStream + + AbstractMediaStream::~AbstractMediaStream() + { ++ delete d_ptr; + } + + qint64 AbstractMediaStream::streamSize() const +--- a/src/3rdparty/phonon/phonon/abstractmediastream.h ++++ b/src/3rdparty/phonon/phonon/abstractmediastream.h +@@ -214,7 +214,7 @@ class PHONON_EXPORT AbstractMediaStream + virtual void seekStream(qint64 offset); + + AbstractMediaStream(AbstractMediaStreamPrivate &dd, QObject *parent); +- QScopedPointer<AbstractMediaStreamPrivate> d_ptr; ++ AbstractMediaStreamPrivate *d_ptr; + }; + + } // namespace Phonon +--- a/src/3rdparty/phonon/phonon/abstractmediastream_p.h ++++ b/src/3rdparty/phonon/phonon/abstractmediastream_p.h +@@ -45,7 +45,6 @@ class PHONON_EXPORT AbstractMediaStreamP + public: + void setStreamInterface(StreamInterface *); + void setMediaObjectPrivate(MediaObjectPrivate *); +- ~AbstractMediaStreamPrivate(); + + protected: + AbstractMediaStreamPrivate() +@@ -57,6 +56,7 @@ class PHONON_EXPORT AbstractMediaStreamP + errorType(NoError) + { + } ++ ~AbstractMediaStreamPrivate(); + + virtual void setStreamSize(qint64 newSize); + virtual void setStreamSeekable(bool s); +--- /dev/null ++++ b/phonon/audiodataoutput.cpp +@@ -0,0 +1,68 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2005 Matthias Kretz <kretz@kde.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#include "audiodataoutput.h" ++#include "audiodataoutput_p.h" ++#include "factory_p.h" ++ ++#define PHONON_CLASSNAME AudioDataOutput ++ ++namespace Phonon ++{ ++ ++PHONON_HEIR_IMPL(AbstractAudioOutput) ++ ++PHONON_GETTER(int, dataSize, d->dataSize) ++PHONON_GETTER(int, sampleRate, -1) ++PHONON_SETTER(setDataSize, dataSize, int) ++ ++bool AudioDataOutputPrivate::aboutToDeleteBackendObject() ++{ ++ Q_ASSERT(m_backendObject); ++ pBACKEND_GET(int, dataSize, "dataSize"); ++ ++ return AbstractAudioOutputPrivate::aboutToDeleteBackendObject(); ++} ++ ++void AudioDataOutputPrivate::setupBackendObject() ++{ ++ Q_Q(AudioDataOutput); ++ Q_ASSERT(m_backendObject); ++ AbstractAudioOutputPrivate::setupBackendObject(); ++ ++ // set up attributes ++ pBACKEND_CALL1("setDataSize", int, dataSize); ++ ++ qRegisterMetaType<QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > >("QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> >"); ++ ++ QObject::connect(m_backendObject, ++ SIGNAL(dataReady(const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &)), ++ q, SIGNAL(dataReady(const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &))); ++ QObject::connect(m_backendObject, SIGNAL(endOfMedia(int)), q, SIGNAL(endOfMedia(int))); ++} ++ ++} // namespace Phonon ++ ++#include "audiodataoutput.moc" ++ ++#undef PHONON_CLASSNAME ++// vim: sw=4 ts=4 tw=80 +--- /dev/null ++++ b/phonon/audiodataoutput.h +@@ -0,0 +1,129 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++#ifndef Phonon_AUDIODATAOUTPUT_H ++#define Phonon_AUDIODATAOUTPUT_H ++ ++#include "phonon_export.h" ++#include "abstractaudiooutput.h" ++#include "phonondefs.h" ++ ++#ifndef DOXYGEN_SHOULD_SKIP_THIS ++template<typename T> class QVector; ++template<typename Key, typename T> class QMap; ++#endif ++ ++namespace Phonon ++{ ++ class AudioDataOutputPrivate; ++ ++ /** ++ * \short This class gives you the audio data (for visualizations). ++ * ++ * This class implements a special AbstractAudioOutput that gives your ++ * application the audio data. Don't expect realtime performance. But ++ * the latencies should be low enough to use the audio data for ++ * visualizations. You can also use the audio data for further processing ++ * (e.g. encoding and saving to a file). ++ * ++ * \author Matthias Kretz <kretz@kde.org> ++ */ ++ class PHONON_EXPORT AudioDataOutput : public AbstractAudioOutput ++ { ++ Q_OBJECT ++ K_DECLARE_PRIVATE(AudioDataOutput) ++ Q_ENUMS(Channel) ++ Q_PROPERTY(int dataSize READ dataSize WRITE setDataSize) ++ PHONON_HEIR(AudioDataOutput) ++ public: ++ /** ++ * Specifies the channel the audio data belongs to. ++ */ ++ enum Channel ++ { ++ LeftChannel, ++ RightChannel, ++ CenterChannel, ++ LeftSurroundChannel, ++ RightSurroundChannel, ++ SubwooferChannel ++ }; ++ ++ /** ++ * Returns the currently used number of samples passed through ++ * the signal. ++ * ++ * \see setDataSize ++ */ ++ int dataSize() const; ++ ++ /** ++ * Returns the sample rate in Hz. Common sample rates are 44100 Hz ++ * and 48000 Hz. AudioDataOutput will not do any sample rate ++ * conversion for you. If you need to convert the sample rate you ++ * might want to take a look at libsamplerate. For visualizations it ++ * is often enough to do simple interpolation or even drop/duplicate ++ * samples. ++ * ++ * \return The sample rate as reported by the backend. If the ++ * backend is unavailable -1 is returned. ++ */ ++ int sampleRate() const; ++ ++ public Q_SLOTS: ++ /** ++ * Sets the number of samples to be passed in one signal emission. ++ * ++ * Defaults to 512 samples per emitted signal. ++ * ++ * \param size the number of samples ++ */ ++ void setDataSize(int size); ++ ++ Q_SIGNALS: ++ /** ++ * Emitted whenever another dataSize number of samples are ready. ++ * ++ * \param data A mapping of Channel to a vector holding the audio data. ++ */ ++ void dataReady(const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &data); ++ ++ ++ /** ++ * This signal is emitted before the last dataReady signal of a ++ * media is emitted. ++ * ++ * If, for example, the playback of a media file has finished and the ++ * last audio data of that file is going to be passed with the next ++ * dataReady signal, and only the 28 first samples of the data ++ * vector are from that media file endOfMedia will be emitted right ++ * before dataReady with \p remainingSamples = 28. ++ * ++ * \param remainingSamples The number of samples in the next ++ * dataReady vector that belong to the media that was playing to ++ * this point. ++ */ ++ void endOfMedia(int remainingSamples); ++ }; ++} // namespace Phonon ++ ++// vim: sw=4 ts=4 tw=80 ++#endif // Phonon_AUDIODATAOUTPUT_H +--- /dev/null ++++ b/phonon/audiodataoutputinterface.h +@@ -0,0 +1,44 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2008 Matthias Kretz <kretz@kde.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#ifndef PHONON_AUDIODATAOUTPUTINTERFACE_H ++#define PHONON_AUDIODATAOUTPUTINTERFACE_H ++ ++namespace Phonon ++{ ++ ++class AudioDataOutput; ++ ++class AudioDataOutputInterface ++{ ++ public: ++ virtual ~AudioDataOutputInterface() {} ++ ++ virtual AudioDataOutput *frontendObject() const = 0; ++ virtual void setFrontendObject(AudioDataOutput *) = 0; ++}; ++ ++} // namespace Phonon ++ ++Q_DECLARE_INTERFACE(Phonon::AudioDataOutputInterface, "0AudioDataOutputInterface.phonon.kde.org") ++ ++#endif // PHONON_AUDIODATAOUTPUTINTERFACE_H +--- /dev/null ++++ b/phonon/audiodataoutput_p.h +@@ -0,0 +1,48 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2006 Matthias Kretz <kretz@kde.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#ifndef AUDIODATAOUTPUT_P_H ++#define AUDIODATAOUTPUT_P_H ++ ++#include "audiodataoutput.h" ++#include "abstractaudiooutput_p.h" ++ ++namespace Phonon ++{ ++ ++class AudioDataOutputPrivate : public AbstractAudioOutputPrivate ++{ ++ Q_DECLARE_PUBLIC(AudioDataOutput) ++ PHONON_PRIVATECLASS ++ protected: ++ AudioDataOutputPrivate() ++ : dataSize(512) ++ { ++ } ++ ++ int dataSize; ++}; ++ ++} // namespace Phonon ++ ++#endif // AUDIODATAOUTPUT_P_H ++// vim: sw=4 ts=4 tw=80 +--- a/src/3rdparty/phonon/phonon/audiooutput.cpp ++++ b/src/3rdparty/phonon/phonon/audiooutput.cpp +@@ -24,10 +24,11 @@ + #include "factory_p.h" + #include "objectdescription.h" + #include "audiooutputadaptor_p.h" +-#include "globalconfig_p.h" ++#include "globalconfig.h" + #include "audiooutputinterface.h" + #include "phononnamespace_p.h" + #include "platform_p.h" ++#include "pulsesupport_p.h" + + #include <QtCore/qmath.h> + +@@ -42,8 +43,12 @@ QT_BEGIN_NAMESPACE + namespace Phonon + { + +-static inline bool callSetOutputDevice(MediaNodePrivate *const d, int index) ++static inline bool callSetOutputDevice(AudioOutputPrivate *const d, int index) + { ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ if (pulse->isActive()) ++ return pulse->setOutputDevice(d->getStreamUuid(), index); ++ + Iface<IFACES2> iface(d); + if (iface) { + return iface->setOutputDevice(AudioOutputDevice::fromIndex(index)); +@@ -51,8 +56,12 @@ static inline bool callSetOutputDevice(M + return Iface<IFACES0>::cast(d)->setOutputDevice(index); + } + +-static inline bool callSetOutputDevice(MediaNodePrivate *const d, const AudioOutputDevice &dev) ++static inline bool callSetOutputDevice(AudioOutputPrivate *const d, const AudioOutputDevice &dev) + { ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ if (pulse->isActive()) ++ return pulse->setOutputDevice(d->getStreamUuid(), dev.index()); ++ + Iface<IFACES2> iface(d); + if (iface) { + return iface->setOutputDevice(dev); +@@ -89,6 +98,8 @@ void AudioOutputPrivate::init(Phonon::Ca + #endif + + category = c; ++ streamUuid = QUuid::createUuid().toString(); ++ PulseSupport::getInstance()->setStreamPropList(category, streamUuid); + + // select hardware device according to the category + device = AudioOutputDevice::fromIndex(GlobalConfig().audioOutputDeviceFor(category, GlobalConfig::AdvancedDevicesFromSettings | GlobalConfig::HideUnavailableDevices)); +@@ -98,7 +109,10 @@ void AudioOutputPrivate::init(Phonon::Ca + q->connect(Factory::sender(), SIGNAL(availableAudioOutputDevicesChanged()), SLOT(_k_deviceListChanged())); + } + +- ++QString AudioOutputPrivate::getStreamUuid() ++{ ++ return streamUuid; ++} + + void AudioOutputPrivate::createBackendObject() + { +@@ -234,7 +248,7 @@ bool AudioOutput::setOutputDevice(const + d->device = newAudioOutputDevice; + } + if (k_ptr->backendObject()) { +- return callSetOutputDevice(k_ptr, d->device.index()); ++ return callSetOutputDevice(d, d->device.index()); + } + return true; + } +@@ -259,7 +273,6 @@ void AudioOutputPrivate::setupBackendObj + // set up attributes + pINTERFACE_CALL(setVolume(pow(volume, VOLTAGE_TO_LOUDNESS_EXPONENT))); + +-#ifndef QT_NO_PHONON_SETTINGSGROUP + // if the output device is not available and the device was not explicitly set + if (!callSetOutputDevice(this, device) && !outputDeviceOverridden) { + // fall back in the preference list of output devices +@@ -267,8 +280,8 @@ void AudioOutputPrivate::setupBackendObj + if (deviceList.isEmpty()) { + return; + } +- for (int i = 0; i < deviceList.count(); ++i) { +- const AudioOutputDevice &dev = AudioOutputDevice::fromIndex(deviceList.at(i)); ++ foreach (int devIndex, deviceList) { ++ const AudioOutputDevice &dev = AudioOutputDevice::fromIndex(devIndex); + if (callSetOutputDevice(this, dev)) { + handleAutomaticDeviceChange(dev, AudioOutputPrivate::FallbackChange); + return; // found one that works +@@ -279,7 +292,6 @@ void AudioOutputPrivate::setupBackendObj + callSetOutputDevice(this, none); + handleAutomaticDeviceChange(none, FallbackChange); + } +-#endif //QT_NO_PHONON_SETTINGSGROUP + } + + void AudioOutputPrivate::_k_volumeChanged(qreal newVolume) +@@ -309,10 +321,8 @@ void AudioOutputPrivate::_k_audioDeviceF + pDebug() << Q_FUNC_INFO; + // outputDeviceIndex identifies a failing device + // fall back in the preference list of output devices +-#ifndef QT_NO_PHONON_SETTINGSGROUP +- const QList<int> deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings | GlobalConfig::HideUnavailableDevices); +- for (int i = 0; i < deviceList.count(); ++i) { +- const int devIndex = deviceList.at(i); ++ QList<int> deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings | GlobalConfig::HideUnavailableDevices); ++ foreach (int devIndex, deviceList) { + // if it's the same device as the one that failed, ignore it + if (device.index() != devIndex) { + const AudioOutputDevice &info = AudioOutputDevice::fromIndex(devIndex); +@@ -322,7 +332,6 @@ void AudioOutputPrivate::_k_audioDeviceF + } + } + } +-#endif //QT_NO_PHONON_SETTINGSGROUP + // if we get here there is no working output device. Tell the backend. + const AudioOutputDevice none; + callSetOutputDevice(this, none); +@@ -332,12 +341,10 @@ void AudioOutputPrivate::_k_audioDeviceF + void AudioOutputPrivate::_k_deviceListChanged() + { + pDebug() << Q_FUNC_INFO; +-#ifndef QT_NO_PHONON_SETTINGSGROUP + // let's see if there's a usable device higher in the preference list +- const QList<int> deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings); ++ QList<int> deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings); + DeviceChangeType changeType = HigherPreferenceChange; +- for (int i = 0; i < deviceList.count(); ++i) { +- const int devIndex = deviceList.at(i); ++ foreach (int devIndex, deviceList) { + const AudioOutputDevice &info = AudioOutputDevice::fromIndex(devIndex); + if (!info.property("available").toBool()) { + if (device.index() == devIndex) { +@@ -358,7 +365,6 @@ void AudioOutputPrivate::_k_deviceListCh + break; // found one with higher preference that works + } + } +-#endif //QT_NO_PHONON_SETTINGSGROUP + } + + static struct +@@ -410,6 +416,7 @@ void AudioOutputPrivate::handleAutomatic + + AudioOutputPrivate::~AudioOutputPrivate() + { ++ PulseSupport::getInstance()->clearStreamCache(streamUuid); + #ifndef QT_NO_DBUS + if (adaptor) { + emit adaptor->outputDestroyed(); +--- a/src/3rdparty/phonon/phonon/audiooutput_p.h ++++ b/src/3rdparty/phonon/phonon/audiooutput_p.h +@@ -46,6 +46,7 @@ class AudioOutputPrivate : public Abstra + return 0; + } + void init(Phonon::Category c); ++ QString getStreamUuid(); + + + protected: +@@ -79,6 +80,7 @@ class AudioOutputPrivate : public Abstra + QString name; + Phonon::AudioOutputDevice device; + qreal volume; ++ QString streamUuid; + #ifndef QT_NO_DBUS + Phonon::AudioOutputAdaptor *adaptor; + #endif +--- /dev/null ++++ b/phonon/backend/Mainpage.dox +@@ -0,0 +1,109 @@ ++/** \mainpage Phonon Backend Interface ++ ++ ++\section phonon_backend_development Backend Development ++If you want to write a new backend for %Phonon this is for you: ++\li \ref phonon_backend_development_page "Phonon Backend Development" ++\li \ref Backend ++ ++\authors ++Matthias Kretz \<kretz@kde.org\> ++ ++\maintainers ++Matthias Kretz \<kretz@kde.org\> ++ ++\licenses ++\lgpl ++ ++ ++ ++ ++ ++ ++ ++ ++\page phonon_tut1 Phonon Tutorial Part 1: a simple audio player ++ ++<p><b> ++\ref index "Overview" | ++Application Example | ++\ref phonon_backend_development_page "Backend Development" ++</b></p> ++ ++\includelineno tutorial2.cpp ++ ++ ++ ++ ++ ++ ++ ++ ++\page phonon_backend_development_page Phonon Backend Development ++ ++<p><b> ++\ref index "Overview" | ++\ref phonon_tut1 "Application Example" | ++Backend Development ++</b></p> ++ ++The backend is the most important part in %Phonon to provide functionality. This ++document will get you started how backends work, how to start development of a ++new backend and how to understand existing backend code. ++ ++\section phonon_backend_introduction Introduction ++ ++The first step is to understand how the %Phonon frontend calls the backend: In ++the frontend objects all backend objects are "only" QObjects. But QObject has ++powerful introspection capabilities that %Phonon uses to call methods in the ++backend. If you're interested look at \ref QMetaObject::invokeMethod. In order ++to make sure that a backend is fully operational (there are no abstract classes ++that tell the backend developer what method signatures are wrong or what ++methods are missing) there are two test programs compiled with kdelibs (if ++KDE4_BUILD_TESTS is set in cmake) that inspects the backend. ++ ++In short that requires the backend classes to inherit from QObject and to make ++all methods that are to be called from the frontend slots or prefixed with ++Q_INVOKABLE (the latter doesn't work reliable with Qt 4.1.3 at least, so you ++should simply make those methods slots). ++ ++\section phonon_backend_classes The Backend Classes ++ ++The central class that needs to be implemented is the backend factory class, ++throughout this documentation simply called Backend: ++ ++\li \ref phonon_Backend "Backend" ++ ++\subsection phonon_mediaproducingclasses The classes producing media data (sources) ++ ++\li \ref Phonon::MediaObjectInterface "common methods/signals for media producing classes" ++\li \ref phonon_MediaObject "MediaObject" ++ ++\subsection phonon_pathclass The path class ++ ++\li \ref phonon_Path "Path" ++ ++\subsection phonon_outputclasses The output classes ++\li \ref phonon_AudioDataOutput "AudioDataOutput" ++\li \ref phonon_AudioOutput "AudioOutput" ++\li \ref phonon_VideoDataOutput "VideoDataOutput" ++\li \ref phonon_VideoWidget "VideoWidget" ++ ++\subsection phonon_EffectClasses The effect classes ++ ++\li \ref phonon_Effect "Effect" ++\li \ref phonon_Visualization "Visualization" ++\li \ref phonon_VolumeFaderEffect "VolumeFaderEffect" ++ ++*/ ++// DOXYGEN_REFERENCES = phonon ++// DOXYGEN_SET_EXPAND_AS_DEFINED = PHONON_OBJECT PHONON_HEIR PHONON_INTERFACE_GETTER ++// DOXYGEN_SET_HIDE_SCOPE_NAMES = YES ++// DOXYGEN_SET_HIDE_FRIEND_COMPOUNDS = YES ++// DOXYGEN_SET_PROJECT_NAME = Backend ++// DOXYGEN_SET_EXTRACT_ALL = NO ++// DOXYGEN_SET_HIDE_UNDOC_MEMBERS = YES ++// DOXYGEN_SET_SOURCE_BROWSER = NO ++// DOXYGEN_SET_INPUT = @topdir@/phonon/phonon ++// DOXYGEN_SET_FILE_PATTERNS = *interface.h */phonon/backend/Mainpage.dox */backend.dox ++// vim: tw=100 ts=4 sw=4 expandtab filetype=doxygen +--- a/src/3rdparty/phonon/phonon/backendcapabilities.cpp ++++ b/src/3rdparty/phonon/phonon/backendcapabilities.cpp +@@ -26,7 +26,7 @@ + #include "phonondefs_p.h" + #include "backendinterface.h" + #include "factory_p.h" +-#include "globalconfig_p.h" ++#include "globalconfig.h" + #include "globalstatic_p.h" + #include "objectdescription.h" + +@@ -75,12 +75,10 @@ bool BackendCapabilities::isMimeTypeAvai + QList<AudioOutputDevice> BackendCapabilities::availableAudioOutputDevices() + { + QList<AudioOutputDevice> ret; +-#ifndef QT_NO_PHONON_SETTINGSGROUP + const QList<int> deviceIndexes = GlobalConfig().audioOutputDeviceListFor(Phonon::NoCategory); +- for (int i = 0; i < deviceIndexes.count(); ++i) { +- ret.append(AudioOutputDevice::fromIndex(deviceIndexes.at(i))); ++ foreach (int i, deviceIndexes) { ++ ret.append(AudioOutputDevice::fromIndex(i)); + } +-#endif //QT_NO_PHONON_SETTINGSGROUP + return ret; + } + +@@ -90,8 +88,8 @@ QList<AudioCaptureDevice> BackendCapabil + { + QList<AudioCaptureDevice> ret; + const QList<int> deviceIndexes = GlobalConfig().audioCaptureDeviceListFor(Phonon::NoCategory); +- for (int i = 0; i < deviceIndexes.count(); ++i) { +- ret.append(AudioCaptureDevice::fromIndex(deviceIndexes.at(i))); ++ foreach (int i, deviceIndexes) { ++ ret.append(AudioCaptureDevice::fromIndex(i)); + } + return ret; + } +@@ -103,9 +101,9 @@ QList<EffectDescription> BackendCapabili + BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend()); + QList<EffectDescription> ret; + if (backendIface) { +- const QList<int> deviceIndexes = backendIface->objectDescriptionIndexes(Phonon::EffectType); +- for (int i = 0; i < deviceIndexes.count(); ++i) { +- ret.append(EffectDescription::fromIndex(deviceIndexes.at(i))); ++ QList<int> deviceIndexes = backendIface->objectDescriptionIndexes(Phonon::EffectType); ++ foreach (int i, deviceIndexes) { ++ ret.append(EffectDescription::fromIndex(i)); + } + } + return ret; +--- a/src/3rdparty/phonon/phonon/backendcapabilities.h ++++ b/src/3rdparty/phonon/phonon/backendcapabilities.h +@@ -15,7 +15,7 @@ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + +- You should have received a copy of the GNU Lesser General Public ++ You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see <http://www.gnu.org/licenses/>. + + */ +@@ -88,18 +88,19 @@ namespace BackendCapabilities + }; + + /** +- * Use this function to get a QObject pointer to connect to one of the Notifier signals. ++ * Use this function to get a QObject pointer to connect to the capabilitiesChanged signal. + * + * \return a pointer to a QObject. + * +- * To connect to the signal do the following: ++ * The capabilitiesChanged signal is emitted if the capabilities have changed. This can ++ * happen if the user has requested a backend change. ++ * ++ * To connect to this signal do the following: + * \code + * QObject::connect(BackendCapabilities::notifier(), SIGNAL(capabilitiesChanged()), ... + * \endcode + * + * \see Notifier::capabilitiesChanged() +- * \see Notifier::availableAudioOutputDevicesChanged() +- * \see Notifier::availableAudioCaptureDevicesChanged() + */ + PHONON_EXPORT Notifier *notifier(); + +--- a/src/3rdparty/phonon/phonon/CMakeLists.txt ++++ b/src/3rdparty/phonon/phonon/CMakeLists.txt +@@ -8,6 +8,22 @@ endif (PHONON_BUILD_EXAMPLES) + + add_subdirectory(experimental) + ++set(PULSEAUDIO_MINIMUM_VERSION "0.9.21") ++macro_optional_find_package(PulseAudio) ++macro_log_feature(PULSEAUDIO_FOUND "PulseAudio" "A cross-platform, networked sound server." "http://www.pulseaudio.org" FALSE "" "Allows audio playback via the PulseAudio soundserver when it is running") ++macro_optional_find_package(GLIB2) ++macro_log_feature(GLIB2_FOUND "GLib2" "GLib 2 is required to compile the pulseaudio for Phonon" "http://www.gtk.org/download/" FALSE) ++ ++if (GLIB2_FOUND AND PULSEAUDIO_FOUND) ++ add_definitions(-DHAVE_PULSEAUDIO) ++ include_directories(${GLIB2_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIR}) ++else(GLIB2_FOUND AND PULSEAUDIO_FOUND) ++ set(PULSEAUDIO_INCLUDE_DIR "") ++ set(PULSEAUDIO_LIBRARY "") ++ set(PULSEAUDIO_MAINLOOP_LIBRARY "") ++endif(GLIB2_FOUND AND PULSEAUDIO_FOUND) ++ ++ + set(phonon_LIB_SRCS + objectdescription.cpp + objectdescriptionmodel.cpp +@@ -35,9 +51,12 @@ set(phonon_LIB_SRCS + videowidget.cpp + videoplayer.cpp + seekslider.cpp ++ swiftslider.cpp + volumeslider.cpp + effectwidget.cpp + iodevicestream.cpp ++ audiodataoutput.cpp ++ pulsesupport.cpp + ) + + if (QT_QTDBUS_FOUND) +@@ -50,6 +69,10 @@ endif (QT_QTDBUS_FOUND) + add_definitions(-DPHONON_LIBRARY_PATH="${PLUGIN_INSTALL_DIR}/plugins") + automoc4_add_library(phonon SHARED ${phonon_LIB_SRCS}) + target_link_libraries(phonon ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}) ++if (GLIB2_FOUND AND PULSEAUDIO_FOUND) ++target_link_libraries(phonon ${GLIB2_LIBRARIES} ${GOBJECT_LIBRARIES} ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY}) ++endif (GLIB2_FOUND AND PULSEAUDIO_FOUND) ++ + if (QT_QTDBUS_FOUND) + target_link_libraries(phonon ${QT_QTDBUS_LIBRARY}) + endif (QT_QTDBUS_FOUND) +@@ -99,6 +122,9 @@ install(FILES + volumeslider.h + effectwidget.h + platformplugin.h ++ audiodataoutput.h ++ audiodataoutputinterface.h ++ globalconfig.h + DESTINATION ${INCLUDE_INSTALL_DIR}/phonon COMPONENT Devel) + + install(FILES org.kde.Phonon.AudioOutput.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) +--- a/src/3rdparty/phonon/phonon/effect.cpp ++++ b/src/3rdparty/phonon/phonon/effect.cpp +@@ -107,8 +107,7 @@ bool EffectPrivate::aboutToDeleteBackend + { + if (m_backendObject) { + const QList<EffectParameter> parameters = pINTERFACE_CALL(parameters()); +- for (int i = 0; i < parameters.count(); ++i) { +- const EffectParameter &p = parameters.at(i); ++ foreach (const EffectParameter &p, parameters) { + parameterValues[p] = pINTERFACE_CALL(parameterValue(p)); + } + } +@@ -121,8 +120,7 @@ void EffectPrivate::setupBackendObject() + + // set up attributes + const QList<EffectParameter> parameters = pINTERFACE_CALL(parameters()); +- for (int i = 0; i < parameters.count(); ++i) { +- const EffectParameter &p = parameters.at(i); ++ foreach (const EffectParameter &p, parameters) { + pINTERFACE_CALL(setParameterValue(p, parameterValues[p])); + } + } +--- a/src/3rdparty/phonon/phonon/effectwidget.cpp ++++ b/src/3rdparty/phonon/phonon/effectwidget.cpp +@@ -97,9 +97,7 @@ void EffectWidgetPrivate::autogenerateUi + Q_Q(EffectWidget); + QVBoxLayout *mainLayout = new QVBoxLayout(q); + mainLayout->setMargin(0); +- const QList<Phonon::EffectParameter> parameters = effect->parameters(); +- for (int i = 0; i < parameters.count(); ++i) { +- const EffectParameter ¶ = parameters.at(i); ++ foreach (const EffectParameter ¶, effect->parameters()) { + QVariant value = effect->parameterValue(para); + QHBoxLayout *pLayout = new QHBoxLayout; + mainLayout->addLayout(pLayout); +@@ -119,14 +117,13 @@ void EffectWidgetPrivate::autogenerateUi + control = cb; + if (value.type() == QVariant::Int) { + //value just defines the item index +- for (int i = 0; i < para.possibleValues().count(); ++i) { +- cb->addItem(para.possibleValues().at(i).toString()); ++ foreach (const QVariant &item, para.possibleValues()) { ++ cb->addItem(item.toString()); + } + cb->setCurrentIndex(value.toInt()); + QObject::connect(cb, SIGNAL(currentIndexChanged(int)), q, SLOT(_k_setIntParameter(int))); + } else { +- for (int i = 0; i < para.possibleValues().count(); ++i) { +- const QVariant &item = para.possibleValues().at(i); ++ foreach (const QVariant &item, para.possibleValues()) { + cb->addItem(item.toString()); + if (item == value) { + cb->setCurrentIndex(cb->count() - 1); +@@ -158,20 +155,18 @@ void EffectWidgetPrivate::autogenerateUi + QObject::connect(sb, SIGNAL(valueChanged(int)), q, SLOT(_k_setIntParameter(int))); + } + break; +- case QMetaType::Float: + case QVariant::Double: + { +- const qreal minValue = para.minimumValue().canConvert(QVariant::Double) ? +- para.minimumValue().toReal() : DEFAULT_MIN; +- const qreal maxValue = para.maximumValue().canConvert(QVariant::Double) ? +- para.maximumValue().toReal() : DEFAULT_MAX; ++ const double minValue = (para.minimumValue().type() == QVariant::Double ? ++ para.minimumValue().toDouble() : DEFAULT_MIN); ++ const double maxValue = (para.maximumValue().type() == QVariant::Double ? ++ para.maximumValue().toDouble() : DEFAULT_MAX); + + if (minValue == -1. && maxValue == 1.) { + //Special case values between -1 and 1.0 to use a slider for improved usability + QSlider *slider = new QSlider(Qt::Horizontal, q); +- control = slider; + slider->setRange(-SLIDER_RANGE, +SLIDER_RANGE); +- slider->setValue(int(SLIDER_RANGE * value.toReal())); ++ slider->setValue(int(SLIDER_RANGE * value.toDouble())); + slider->setTickPosition(QSlider::TicksBelow); + slider->setTickInterval(TICKINTERVAL); + QObject::connect(slider, SIGNAL(valueChanged(int)), q, SLOT(_k_setSliderParameter(int))); +@@ -193,10 +188,10 @@ void EffectWidgetPrivate::autogenerateUi + break; + } + +- if (control) { + #ifndef QT_NO_TOOLTIP + control->setToolTip(para.description()); + #endif ++ if (control) { + #ifndef QT_NO_SHORTCUT + label->setBuddy(control); + #endif +--- a/src/3rdparty/phonon/phonon/factory.cpp ++++ b/src/3rdparty/phonon/phonon/factory.cpp +@@ -111,7 +111,6 @@ void Factory::setBackend(QObject *b) + + bool FactoryPrivate::createBackend() + { +-#ifndef QT_NO_LIBRARY + Q_ASSERT(m_backendObject == 0); + #ifndef QT_NO_PHONON_PLATFORMPLUGIN + PlatformPlugin *f = globalFactory->platformPlugin(); +@@ -125,39 +124,15 @@ bool FactoryPrivate::createBackend() + // could not load a backend through the platform plugin. Falling back to the default + // (finding the first loadable backend). + const QLatin1String suffix("/phonon_backend/"); +- const QStringList paths = QCoreApplication::libraryPaths(); +- for (int i = 0; i < paths.count(); ++i) { +- const QString libPath = paths.at(i) + suffix; ++ foreach (QString libPath, QCoreApplication::libraryPaths()) { ++ libPath += suffix; + const QDir dir(libPath); + if (!dir.exists()) { + pDebug() << Q_FUNC_INFO << dir.absolutePath() << "does not exist"; + continue; + } +- +- QStringList plugins(dir.entryList(QDir::Files)); +- +-#ifdef Q_OS_SYMBIAN +- /* On Symbian OS we might have two plugins, one which uses Symbian +- * MMF framework("phonon_mmf"), and one which uses Real Networks's +- * Helix("hxphonon"). We prefer the latter because it's more +- * sophisticated, so we make sure the Helix backend is attempted +- * to be loaded first, and the MMF backend is used for backup. */ +- { +- +- const int hxphonon = plugins.indexOf(QLatin1String("hxphonon")); +- if (hxphonon != -1) +- plugins.move(hxphonon, 0); +- +- // Code for debugging the MMF backend. +- if(hxphonon != -1) { +- qDebug() << "Found hxphonon backend and removed it from the lookup list."; +- plugins.removeAll(QLatin1String("hxphonon")); +- } +- } +-#endif +- +- for (int i = 0; i < plugins.count(); ++i) { +- QPluginLoader pluginLoader(libPath + plugins.at(i)); ++ foreach (const QString &pluginName, dir.entryList(QDir::Files)) { ++ QPluginLoader pluginLoader(libPath + pluginName); + if (!pluginLoader.load()) { + pDebug() << Q_FUNC_INFO << " load failed:" + << pluginLoader.errorString(); +@@ -187,20 +162,14 @@ bool FactoryPrivate::createBackend() + SLOT(objectDescriptionChanged(ObjectDescriptionType))); + + return true; +-#else //QT_NO_LIBRARY +- pWarning() << Q_FUNC_INFO << "Trying to use Phonon with QT_NO_LIBRARY defined. " +- "That is currently not supported"; +- return false; +-#endif + } + + FactoryPrivate::FactoryPrivate() +- : + #ifndef QT_NO_PHONON_PLATFORMPLUGIN +- m_platformPlugin(0), +- m_noPlatformPlugin(false), ++ : m_platformPlugin(0), ++ m_noPlatformPlugin(false) + #endif //QT_NO_PHONON_PLATFORMPLUGIN +- m_backendObject(0) ++ , m_backendObject(0) + { + // Add the post routine to make sure that all other global statics (especially the ones from Qt) + // are still available. If the FactoryPrivate dtor is called too late many bad things can happen +@@ -214,8 +183,14 @@ FactoryPrivate::FactoryPrivate() + + FactoryPrivate::~FactoryPrivate() + { +- for (int i = 0; i < mediaNodePrivateList.count(); ++i) { +- mediaNodePrivateList.at(i)->deleteBackendObject(); ++ foreach (QObject *o, objects) { ++ MediaObject *m = qobject_cast<MediaObject *>(o); ++ if (m) { ++ m->stop(); ++ } ++ } ++ foreach (MediaNodePrivate *bp, mediaNodePrivateList) { ++ bp->deleteBackendObject(); + } + if (objects.size() > 0) { + pError() << "The backend objects are not deleted as was requested."; +@@ -283,8 +258,8 @@ void Factory::deregisterFrontendObject(M + void FactoryPrivate::phononBackendChanged() + { + if (m_backendObject) { +- for (int i = 0; i < mediaNodePrivateList.count(); ++i) { +- mediaNodePrivateList.at(i)->deleteBackendObject(); ++ foreach (MediaNodePrivate *bp, mediaNodePrivateList) { ++ bp->deleteBackendObject(); + } + if (objects.size() > 0) { + pDebug() << "WARNING: we were asked to change the backend but the application did\n" +@@ -293,8 +268,8 @@ void FactoryPrivate::phononBackendChange + "backendswitching possible."; + // in case there were objects deleted give 'em a chance to recreate + // them now +- for (int i = 0; i < mediaNodePrivateList.count(); ++i) { +- mediaNodePrivateList.at(i)->createBackendObject(); ++ foreach (MediaNodePrivate *bp, mediaNodePrivateList) { ++ bp->createBackendObject(); + } + return; + } +@@ -302,8 +277,8 @@ void FactoryPrivate::phononBackendChange + m_backendObject = 0; + } + createBackend(); +- for (int i = 0; i < mediaNodePrivateList.count(); ++i) { +- mediaNodePrivateList.at(i)->createBackendObject(); ++ foreach (MediaNodePrivate *bp, mediaNodePrivateList) { ++ bp->createBackendObject(); + } + emit backendChanged(); + } +@@ -350,6 +325,7 @@ FACTORY_IMPL(AudioOutput) + #ifndef QT_NO_PHONON_VIDEO + FACTORY_IMPL(VideoWidget) + #endif //QT_NO_PHONON_VIDEO ++FACTORY_IMPL(AudioDataOutput) + + #undef FACTORY_IMPL + +@@ -387,17 +363,15 @@ PlatformPlugin *FactoryPrivate::platform + QStringList()) + ); + dir.setFilter(QDir::Files); +- const QStringList libPaths = QCoreApplication::libraryPaths(); + forever { +- for (int i = 0; i < libPaths.count(); ++i) { +- const QString libPath = libPaths.at(i) + suffix; ++ foreach (QString libPath, QCoreApplication::libraryPaths()) { ++ libPath += suffix; + dir.setPath(libPath); + if (!dir.exists()) { + continue; + } +- const QStringList files = dir.entryList(QDir::Files); +- for (int i = 0; i < files.count(); ++i) { +- QPluginLoader pluginLoader(libPath + files.at(i)); ++ foreach (const QString &pluginName, dir.entryList()) { ++ QPluginLoader pluginLoader(libPath + pluginName); + if (!pluginLoader.load()) { + pDebug() << Q_FUNC_INFO << " platform plugin load failed:" + << pluginLoader.errorString(); +@@ -449,7 +423,6 @@ QObject *Factory::backend(bool createWhe + return globalFactory->m_backendObject; + } + +-#ifndef QT_NO_PROPERTIES + #define GET_STRING_PROPERTY(name) \ + QString Factory::name() \ + { \ +@@ -465,11 +438,11 @@ GET_STRING_PROPERTY(backendComment) + GET_STRING_PROPERTY(backendVersion) + GET_STRING_PROPERTY(backendIcon) + GET_STRING_PROPERTY(backendWebsite) +-#endif //QT_NO_PROPERTIES ++ + QObject *Factory::registerQObject(QObject *o) + { + if (o) { +- QObject::connect(o, SIGNAL(destroyed(QObject*)), globalFactory, SLOT(objectDestroyed(QObject*)), Qt::DirectConnection); ++ QObject::connect(o, SIGNAL(destroyed(QObject *)), globalFactory, SLOT(objectDestroyed(QObject *)), Qt::DirectConnection); + globalFactory->objects.append(o); + } + return o; +--- a/src/3rdparty/phonon/phonon/factory_p.h ++++ b/src/3rdparty/phonon/phonon/factory_p.h +@@ -122,6 +122,13 @@ namespace Factory + #endif //QT_NO_PHONON_VIDEO + + /** ++ * Create a new backend object for a AudioDataOutput. ++ * ++ * \return a pointer to the AudioDataOutput the backend provides. ++ */ ++ PHONON_EXPORT QObject *createAudioDataOutput(QObject *parent = 0); ++ ++ /** + * \return a pointer to the backend interface. + */ + PHONON_EXPORT QObject *backend(bool createWhenNull = true); +--- a/src/3rdparty/phonon/phonon/globalconfig.cpp ++++ b/src/3rdparty/phonon/phonon/globalconfig.cpp +@@ -20,6 +20,7 @@ + + */ + ++#include "globalconfig.h" + #include "globalconfig_p.h" + + #include "factory_p.h" +@@ -29,6 +30,7 @@ + #include "backendinterface.h" + #include "qsettingsgroup_p.h" + #include "phononnamespace_p.h" ++#include "pulsesupport_p.h" + + #include <QtCore/QList> + #include <QtCore/QVariant> +@@ -38,15 +40,18 @@ QT_BEGIN_NAMESPACE + namespace Phonon + { + ++GlobalConfigPrivate::GlobalConfigPrivate() : config(QLatin1String("kde.org"), QLatin1String("libphonon")) ++{ ++} ++ + GlobalConfig::GlobalConfig() +-#ifndef QT_NO_SETTINGS +- : m_config(QLatin1String("kde.org"), QLatin1String("libphonon")) +-#endif //QT_NO_SETTINGS ++ : k_ptr(new GlobalConfigPrivate) + { + } + + GlobalConfig::~GlobalConfig() + { ++ delete k_ptr; + } + + enum WhatToFilter { +@@ -85,9 +90,12 @@ static void filter(ObjectDescriptionType + } + } + +-#ifndef QT_NO_PHONON_SETTINGSGROUP +-static QList<int> listSortedByConfig(const QSettingsGroup &backendConfig, Phonon::Category category, QList<int> &defaultList) ++static QList<int> sortDevicesByCategoryPriority(const GlobalConfig *config, const QSettingsGroup *backendConfig, ObjectDescriptionType type, Phonon::Category category, QList<int> &defaultList) + { ++ Q_ASSERT(config); ++ Q_ASSERT(backendConfig); ++ Q_ASSERT(type == AudioOutputDeviceType || type == AudioCaptureDeviceType); ++ + if (defaultList.size() <= 1) { + // nothing to sort + return defaultList; +@@ -104,20 +112,26 @@ static QList<int> listSortedByConfig(con + } + } + +- QString categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(category)); +- if (!backendConfig.hasKey(categoryKey)) { +- // no list in config for the given category +- categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(Phonon::NoCategory)); +- if (!backendConfig.hasKey(categoryKey)) { +- // no list in config for NoCategory +- return defaultList; ++ QList<int> deviceList; ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ if (pulse->isActive()) { ++ deviceList = pulse->objectIndexesByCategory(type, category); ++ } else { ++ QString categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(category)); ++ if (!backendConfig->hasKey(categoryKey)) { ++ // no list in config for the given category ++ categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(Phonon::NoCategory)); ++ if (!backendConfig->hasKey(categoryKey)) { ++ // no list in config for NoCategory ++ return defaultList; ++ } + } +- } + +- //Now the list from m_config +- QList<int> deviceList = backendConfig.value(categoryKey, QList<int>()); ++ //Now the list from d->config ++ deviceList = backendConfig->value(categoryKey, QList<int>()); ++ } + +- //if there are devices in m_config that the backend doesn't report, remove them from the list ++ //if there are devices in d->config that the backend doesn't report, remove them from the list + QMutableListIterator<int> i(deviceList); + while (i.hasNext()) { + if (0 == defaultList.removeAll(i.next())) { +@@ -125,49 +139,176 @@ static QList<int> listSortedByConfig(con + } + } + +- //if the backend reports more devices that are not in m_config append them to the list ++ //if the backend reports more devices that are not in d->config append them to the list + deviceList += defaultList; + + return deviceList; + } +-#endif //QT_NO_PHONON_SETTINGSGROUP + +-#ifndef QT_NO_PHONON_SETTINGSGROUP +-QList<int> GlobalConfig::audioOutputDeviceListFor(Phonon::Category category, int override) const ++bool GlobalConfig::hideAdvancedDevices() const + { ++ K_D(const GlobalConfig); + //The devices need to be stored independently for every backend +- const QSettingsGroup backendConfig(&m_config, QLatin1String("AudioOutputDevice")); // + Factory::identifier()); +- const QSettingsGroup generalGroup(&m_config, QLatin1String("General")); +- const bool hideAdvancedDevices = ((override & AdvancedDevicesFromSettings) +- ? generalGroup.value(QLatin1String("HideAdvancedDevices"), true) ++ const QSettingsGroup generalGroup(&d->config, QLatin1String("General")); ++ return generalGroup.value(QLatin1String("HideAdvancedDevices"), true); ++} ++ ++void GlobalConfig::setHideAdvancedDevices(bool hide) ++{ ++ K_D(GlobalConfig); ++ QSettingsGroup generalGroup(&d->config, QLatin1String("General")); ++ generalGroup.setValue(QLatin1String("HideAdvancedDevices"), hide); ++} ++ ++static bool isHiddenAudioOutputDevice(const GlobalConfig *config, int i) ++{ ++ Q_ASSERT(config); ++ ++ if (!config->hideAdvancedDevices()) ++ return false; ++ ++ AudioOutputDevice ad = AudioOutputDevice::fromIndex(i); ++ const QVariant var = ad.property("isAdvanced"); ++ return (var.isValid() && var.toBool()); ++} ++ ++#ifndef QT_NO_PHONON_AUDIOCAPTURE ++static bool isHiddenAudioCaptureDevice(const GlobalConfig *config, int i) ++{ ++ Q_ASSERT(config); ++ ++ if (!config->hideAdvancedDevices()) ++ return false; ++ ++ AudioCaptureDevice ad = AudioCaptureDevice::fromIndex(i); ++ const QVariant var = ad.property("isAdvanced"); ++ return (var.isValid() && var.toBool()); ++} ++#endif ++ ++static QList<int> reindexList(const GlobalConfig *config, Phonon::Category category, QList<int>newOrder, bool output) ++{ ++ Q_ASSERT(config); ++#ifdef QT_NO_PHONON_AUDIOCAPTURE ++ Q_ASSERT(output); ++#endif ++ ++ /*QString sb; ++ sb = QString("(Size %1)").arg(currentList.size()); ++ foreach (int i, currentList) ++ sb += QString("%1, ").arg(i); ++ fprintf(stderr, "=== Reindex Current: %s\n", sb.toUtf8().constData()); ++ sb = QString("(Size %1)").arg(newOrder.size()); ++ foreach (int i, newOrder) ++ sb += QString("%1, ").arg(i); ++ fprintf(stderr, "=== Reindex Before : %s\n", sb.toUtf8().constData());*/ ++ ++ QList<int> currentList; ++ if (output) ++ currentList = config->audioOutputDeviceListFor(category, GlobalConfig::ShowUnavailableDevices|GlobalConfig::ShowAdvancedDevices); ++#ifndef QT_NO_PHONON_AUDIOCAPTURE ++ else ++ currentList = config->audioCaptureDeviceListFor(category, GlobalConfig::ShowUnavailableDevices|GlobalConfig::ShowAdvancedDevices); ++#endif ++ ++ QList<int> newList; ++ ++ foreach (int i, newOrder) { ++ int found = currentList.indexOf(i); ++ if (found < 0) { ++ // It's not in the list, so something is odd (e.g. client error). Ignore it. ++ continue; ++ } ++ ++ // Iterate through the list from this point onward. If there are hidden devices ++ // immediately following, take them too. ++ newList.append(currentList.takeAt(found)); ++ while (found < currentList.size()) { ++ bool hidden = true; ++ if (output) ++ hidden = isHiddenAudioOutputDevice(config, currentList.at(found)); ++#ifndef QT_NO_PHONON_AUDIOCAPTURE ++ else ++ hidden = isHiddenAudioCaptureDevice(config, currentList.at(found)); ++#endif ++ ++ if (!hidden) ++ break; ++ newList.append(currentList.takeAt(found)); ++ } ++ } ++ ++ // If there are any devices left in.. just tack them on the end. ++ if (currentList.size() > 0) ++ newList += currentList; ++ ++ /*sb = QString("(Size %1)").arg(newList.size()); ++ foreach (int i, newList) ++ sb += QString("%1, ").arg(i); ++ fprintf(stderr, "=== Reindex After : %s\n", sb.toUtf8().constData());*/ ++ return newList; ++} ++ ++ ++void GlobalConfig::setAudioOutputDeviceListFor(Phonon::Category category, QList<int> order) ++{ ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ if (pulse->isActive()) { ++ pulse->setOutputDevicePriorityForCategory(category, order); ++ return; ++ } ++ ++ K_D(GlobalConfig); ++ QSettingsGroup backendConfig(&d->config, QLatin1String("AudioOutputDevice")); // + Factory::identifier()); ++ ++ order = reindexList(this, category, order, true); ++ ++ const QList<int> noCategoryOrder = audioOutputDeviceListFor(Phonon::NoCategory, ShowUnavailableDevices|ShowAdvancedDevices); ++ if (category != Phonon::NoCategory && order == noCategoryOrder) { ++ backendConfig.removeEntry(QLatin1String("Category_") + QString::number(category)); ++ } else { ++ backendConfig.setValue(QLatin1String("Category_") + QString::number(category), order); ++ } ++} ++ ++QList<int> GlobalConfig::audioOutputDeviceListFor(Phonon::Category category, int override) const ++{ ++ K_D(const GlobalConfig); ++ ++ const bool hide = ((override & AdvancedDevicesFromSettings) ++ ? hideAdvancedDevices() + : static_cast<bool>(override & HideAdvancedDevices)); + + QList<int> defaultList; ++ BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend()); ++ + #ifndef QT_NO_PHONON_PLATFORMPLUGIN +- if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) { +- // the platform plugin lists the audio devices for the platform +- // this list already is in default order (as defined by the platform plugin) +- defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioOutputDeviceType); +- if (hideAdvancedDevices) { +- QMutableListIterator<int> it(defaultList); +- while (it.hasNext()) { +- AudioOutputDevice objDesc = AudioOutputDevice::fromIndex(it.next()); +- const QVariant var = objDesc.property("isAdvanced"); +- if (var.isValid() && var.toBool()) { +- it.remove(); ++ if (!backendIface || !PulseSupport::getInstance()->isActive()) { ++ if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) { ++ // the platform plugin lists the audio devices for the platform ++ // this list already is in default order (as defined by the platform plugin) ++ defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioOutputDeviceType); ++ if (hide) { ++ QMutableListIterator<int> it(defaultList); ++ while (it.hasNext()) { ++ AudioOutputDevice objDesc = AudioOutputDevice::fromIndex(it.next()); ++ const QVariant var = objDesc.property("isAdvanced"); ++ if (var.isValid() && var.toBool()) { ++ it.remove(); ++ } + } + } + } + } + #endif //QT_NO_PHONON_PLATFORMPLUGIN + +- // lookup the available devices directly from the backend (mostly for virtual devices) +- if (BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend())) { ++ // lookup the available devices directly from the backend ++ if (backendIface) { + // this list already is in default order (as defined by the backend) + QList<int> list = backendIface->objectDescriptionIndexes(Phonon::AudioOutputDeviceType); +- if (hideAdvancedDevices || !defaultList.isEmpty() || (override & HideUnavailableDevices)) { ++ if (hide || !defaultList.isEmpty() || (override & HideUnavailableDevices)) { + filter(AudioOutputDeviceType, backendIface, &list, +- (hideAdvancedDevices ? FilterAdvancedDevices : 0) ++ (hide ? FilterAdvancedDevices : 0) + // the platform plugin already provided the hardware devices + | (defaultList.isEmpty() ? 0 : FilterHardwareDevices) + | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0) +@@ -176,56 +317,78 @@ QList<int> GlobalConfig::audioOutputDevi + defaultList += list; + } + +- return listSortedByConfig(backendConfig, category, defaultList); ++ const QSettingsGroup backendConfig(&d->config, QLatin1String("AudioOutputDevice")); // + Factory::identifier()); ++ return sortDevicesByCategoryPriority(this, &backendConfig, AudioOutputDeviceType, category, defaultList); + } +-#endif //QT_NO_PHONON_SETTINGSGROUP ++ + int GlobalConfig::audioOutputDeviceFor(Phonon::Category category, int override) const + { +-#ifndef QT_NO_PHONON_SETTINGSGROUP + QList<int> ret = audioOutputDeviceListFor(category, override); +- if (!ret.isEmpty()) +- return ret.first(); +-#endif //QT_NO_PHONON_SETTINGSGROUP +- return -1; ++ if (ret.isEmpty()) ++ return -1; ++ return ret.first(); + } + + #ifndef QT_NO_PHONON_AUDIOCAPTURE ++void GlobalConfig::setAudioCaptureDeviceListFor(Phonon::Category category, QList<int> order) ++{ ++ PulseSupport *pulse = PulseSupport::getInstance(); ++ if (pulse->isActive()) { ++ pulse->setCaptureDevicePriorityForCategory(category, order); ++ return; ++ } ++ ++ K_D(GlobalConfig); ++ QSettingsGroup backendConfig(&d->config, QLatin1String("AudioCaptureDevice")); // + Factory::identifier()); ++ ++ order = reindexList(this, category, order, false); ++ ++ const QList<int> noCategoryOrder = audioCaptureDeviceListFor(Phonon::NoCategory, ShowUnavailableDevices|ShowAdvancedDevices); ++ if (category != Phonon::NoCategory && order == noCategoryOrder) { ++ backendConfig.removeEntry(QLatin1String("Category_") + QString::number(category)); ++ } else { ++ backendConfig.setValue(QLatin1String("Category_") + QString::number(category), order); ++ } ++} ++ + QList<int> GlobalConfig::audioCaptureDeviceListFor(Phonon::Category category, int override) const + { +-#ifndef QT_NO_PHONON_SETTINGSGROUP +- //The devices need to be stored independently for every backend +- const QSettingsGroup backendConfig(&m_config, QLatin1String("AudioCaptureDevice")); // + Factory::identifier()); +- const QSettingsGroup generalGroup(&m_config, QLatin1String("General")); +- const bool hideAdvancedDevices = ((override & AdvancedDevicesFromSettings) +- ? generalGroup.value(QLatin1String("HideAdvancedDevices"), true) ++ K_D(const GlobalConfig); ++ ++ const bool hide = ((override & AdvancedDevicesFromSettings) ++ ? hideAdvancedDevices() + : static_cast<bool>(override & HideAdvancedDevices)); + + QList<int> defaultList; ++ BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend()); ++ + #ifndef QT_NO_PHONON_PLATFORMPLUGIN +- if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) { +- // the platform plugin lists the audio devices for the platform +- // this list already is in default order (as defined by the platform plugin) +- defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType); +- if (hideAdvancedDevices) { +- QMutableListIterator<int> it(defaultList); +- while (it.hasNext()) { +- AudioCaptureDevice objDesc = AudioCaptureDevice::fromIndex(it.next()); +- const QVariant var = objDesc.property("isAdvanced"); +- if (var.isValid() && var.toBool()) { +- it.remove(); ++ if (!backendIface || !PulseSupport::getInstance()->isActive()) { ++ if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) { ++ // the platform plugin lists the audio devices for the platform ++ // this list already is in default order (as defined by the platform plugin) ++ defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType); ++ if (hide) { ++ QMutableListIterator<int> it(defaultList); ++ while (it.hasNext()) { ++ AudioCaptureDevice objDesc = AudioCaptureDevice::fromIndex(it.next()); ++ const QVariant var = objDesc.property("isAdvanced"); ++ if (var.isValid() && var.toBool()) { ++ it.remove(); ++ } + } + } + } + } + #endif //QT_NO_PHONON_PLATFORMPLUGIN + +- // lookup the available devices directly from the backend (mostly for virtual devices) +- if (BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend())) { ++ // lookup the available devices directly from the backend ++ if (backendIface) { + // this list already is in default order (as defined by the backend) + QList<int> list = backendIface->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType); +- if (hideAdvancedDevices || !defaultList.isEmpty() || (override & HideUnavailableDevices)) { ++ if (hide || !defaultList.isEmpty() || (override & HideUnavailableDevices)) { + filter(AudioCaptureDeviceType, backendIface, &list, +- (hideAdvancedDevices ? FilterAdvancedDevices : 0) ++ (hide ? FilterAdvancedDevices : 0) + // the platform plugin already provided the hardware devices + | (defaultList.isEmpty() ? 0 : FilterHardwareDevices) + | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0) +@@ -234,10 +397,8 @@ QList<int> GlobalConfig::audioCaptureDev + defaultList += list; + } + +- return listSortedByConfig(backendConfig, category, defaultList); +-#else //QT_NO_SETTINGSGROUP +- return QList<int>(); +-#endif //QT_NO_SETTINGSGROUP ++ const QSettingsGroup backendConfig(&d->config, QLatin1String("AudioCaptureDevice")); // + Factory::identifier()); ++ return sortDevicesByCategoryPriority(this, &backendConfig, AudioCaptureDeviceType, category, defaultList); + } + + int GlobalConfig::audioCaptureDeviceFor(Phonon::Category category, int override) const +--- /dev/null ++++ b/phonon/globalconfig.h +@@ -0,0 +1,71 @@ ++/* This file is part of the KDE project ++Copyright (C) 2006-2008 Matthias Kretz <kretz@kde.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#ifndef PHONON_GLOBALCONFIG_H ++#define PHONON_GLOBALCONFIG_H ++ ++#include "phonon_export.h" ++#include "phononnamespace.h" ++#include "phonondefs.h" ++ ++QT_BEGIN_HEADER ++QT_BEGIN_NAMESPACE ++ ++namespace Phonon ++{ ++ class GlobalConfigPrivate; ++ ++ class PHONON_EXPORT GlobalConfig ++ { ++ K_DECLARE_PRIVATE(GlobalConfig) ++ public: ++ GlobalConfig(); ++ virtual ~GlobalConfig(); ++ ++ enum DevicesToHideFlag { ++ ShowUnavailableDevices = 0, ++ ShowAdvancedDevices = 0, ++ HideAdvancedDevices = 1, ++ AdvancedDevicesFromSettings = 2, ++ HideUnavailableDevices = 4 ++ }; ++ bool hideAdvancedDevices() const; ++ void setHideAdvancedDevices(bool hide = true); ++ void setAudioOutputDeviceListFor(Phonon::Category category, QList<int> order); ++ QList<int> audioOutputDeviceListFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; ++ int audioOutputDeviceFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; ++ ++#ifndef QT_NO_PHONON_AUDIOCAPTURE ++ void setAudioCaptureDeviceListFor(Phonon::Category category, QList<int> order); ++ QList<int> audioCaptureDeviceListFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; ++ int audioCaptureDeviceFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; ++#endif //QT_NO_PHONON_AUDIOCAPTURE ++ ++ protected: ++ GlobalConfigPrivate *const k_ptr; ++ }; ++} // namespace Phonon ++ ++QT_END_NAMESPACE ++QT_END_HEADER ++ ++#endif // PHONON_GLOBALCONFIG_H +--- a/src/3rdparty/phonon/phonon/globalconfig_p.h ++++ b/src/3rdparty/phonon/phonon/globalconfig_p.h +@@ -26,40 +26,19 @@ Copyright (C) 2006-2008 Matthias Kretz < + #include <QtCore/QSettings> + + #include "phonon_export.h" +-#include "phononnamespace.h" + + QT_BEGIN_HEADER + QT_BEGIN_NAMESPACE + + namespace Phonon + { +- class PHONON_EXPORT GlobalConfig ++ class GlobalConfigPrivate + { +- public: +- GlobalConfig(); +- virtual ~GlobalConfig(); +- +- enum DevicesToHideFlag { +- ShowUnavailableDevices = 0, +- ShowAdvancedDevices = 0, +- HideAdvancedDevices = 1, +- AdvancedDevicesFromSettings = 2, +- HideUnavailableDevices = 4 +- }; +-#ifndef QT_NO_PHONON_SETTINGSGROUP +- QList<int> audioOutputDeviceListFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; +-#endif //QT_NO_PHONON_SETTINGSGROUP +- int audioOutputDeviceFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; +- +-#ifndef QT_NO_PHONON_AUDIOCAPTURE +- QList<int> audioCaptureDeviceListFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; +- int audioCaptureDeviceFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; +-#endif //QT_NO_PHONON_AUDIOCAPTURE +- +- protected: +-#ifndef QT_NO_SETTINGS +- QSettings m_config; +-#endif //QT_NO_SETTINGS ++ public: ++ GlobalConfigPrivate(); ++ virtual ~GlobalConfigPrivate() {} ++ ++ QSettings config; + }; + } // namespace Phonon + +--- a/src/3rdparty/phonon/phonon/medianode.cpp ++++ b/src/3rdparty/phonon/phonon/medianode.cpp +@@ -67,8 +67,8 @@ bool MediaNode::isValid() const + + MediaNodePrivate::~MediaNodePrivate() + { +- for (int i = 0 ; i < handlers.count(); ++i) { +- handlers.at(i)->phononObjectDestroyed(this); ++ foreach (MediaNodeDestructionHandler *handler, handlers) { ++ handler->phononObjectDestroyed(this); + } + Factory::deregisterFrontendObject(this); + delete m_backendObject; +--- a/src/3rdparty/phonon/phonon/mediaobject.cpp ++++ b/src/3rdparty/phonon/phonon/mediaobject.cpp +@@ -300,15 +300,15 @@ void MediaObject::enqueue(const MediaSou + + void MediaObject::enqueue(const QList<MediaSource> &sources) + { +- for (int i = 0; i < sources.count(); ++i) { +- enqueue(sources.at(i)); ++ foreach (const MediaSource &m, sources) { ++ enqueue(m); + } + } + + void MediaObject::enqueue(const QList<QUrl> &urls) + { +- for (int i = 0; i < urls.count(); ++i) { +- enqueue(urls.at(i)); ++ foreach (const QUrl &url, urls) { ++ enqueue(url); + } + } + +@@ -453,9 +453,9 @@ void MediaObjectPrivate::setupBackendObj + //pDebug() << Q_FUNC_INFO; + + #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM +- QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), q, SLOT(_k_stateChanged(Phonon::State,Phonon::State))); ++ QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), q, SLOT(_k_stateChanged(Phonon::State, Phonon::State))); + #else +- QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), q, SIGNAL(stateChanged(Phonon::State,Phonon::State))); ++ QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), q, SIGNAL(stateChanged(Phonon::State, Phonon::State))); + #endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM + QObject::connect(m_backendObject, SIGNAL(tick(qint64)), q, SIGNAL(tick(qint64))); + QObject::connect(m_backendObject, SIGNAL(seekableChanged(bool)), q, SIGNAL(seekableChanged(bool))); +@@ -467,10 +467,10 @@ void MediaObjectPrivate::setupBackendObj + QObject::connect(m_backendObject, SIGNAL(aboutToFinish()), q, SLOT(_k_aboutToFinish())); + QObject::connect(m_backendObject, SIGNAL(prefinishMarkReached(qint32)), q, SIGNAL(prefinishMarkReached(qint32))); + QObject::connect(m_backendObject, SIGNAL(totalTimeChanged(qint64)), q, SIGNAL(totalTimeChanged(qint64))); +- QObject::connect(m_backendObject, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)), +- q, SLOT(_k_metaDataChanged(QMultiMap<QString,QString>))); +- QObject::connect(m_backendObject, SIGNAL(currentSourceChanged(MediaSource)), +- q, SLOT(_k_currentSourceChanged(MediaSource))); ++ QObject::connect(m_backendObject, SIGNAL(metaDataChanged(const QMultiMap<QString, QString> &)), ++ q, SLOT(_k_metaDataChanged(const QMultiMap<QString, QString> &))); ++ QObject::connect(m_backendObject, SIGNAL(currentSourceChanged(const MediaSource&)), ++ q, SLOT(_k_currentSourceChanged(const MediaSource&))); + + // set up attributes + pINTERFACE_CALL(setTickInterval(tickInterval)); +@@ -502,8 +502,8 @@ void MediaObjectPrivate::setupBackendObj + } + + #ifndef QT_NO_PHONON_MEDIACONTROLLER +- for (int i = 0 ; i < interfaceList.count(); ++i) { +- interfaceList.at(i)->_backendObjectChanged(); ++ foreach (FrontendInterfacePrivate *f, interfaceList) { ++ f->_backendObjectChanged(); + } + #endif //QT_NO_PHONON_MEDIACONTROLLER + +--- a/src/3rdparty/phonon/phonon/mediaobject.h ++++ b/src/3rdparty/phonon/phonon/mediaobject.h +@@ -15,7 +15,7 @@ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + +- You should have received a copy of the GNU Lesser General Public ++ You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see <http://www.gnu.org/licenses/>. + + */ +@@ -198,18 +198,18 @@ namespace Phonon + * Check whether the current media may be seeked. + * + * \warning This information cannot be known immediately. It is best +- * to also listen to the seekableChanged signal. ++ * to also listen to the hasVideoChanged signal. + * + * \code +- * connect(media, SIGNAL(seekableChanged(bool)), seekableChanged(bool)); ++ * connect(media, SIGNAL(hasVideoChanged(bool)), hasVideoChanged(bool)); + * media->setCurrentSource("somevideo.avi"); +- * media->isSeekable(); // returns false; ++ * media->hasVideo(); // returns false; + * } + * +- * void seekableChanged(bool b) ++ * void hasVideoChanged(bool b) + * { + * // b == true +- * media->isSeekable(); // returns true; ++ * media->hasVideo(); // returns true; + * } + * \endcode + * +@@ -301,7 +301,7 @@ namespace Phonon + void setCurrentSource(const MediaSource &source); + + /** +- * Returns the queued media sources. This list does not include ++ * Returns the queued media sources. This does list does not include + * the current source (returned by currentSource). + */ + QList<MediaSource> queue() const; +@@ -456,6 +456,8 @@ namespace Phonon + Q_SIGNALS: + /** + * Emitted when the state of the MediaObject has changed. ++ * In case you're not interested in the old state you can also ++ * connect to a slot that only has one State argument. + * + * @param newstate The state the Player is in now. + * @param oldstate The state the Player was in before. +@@ -585,7 +587,7 @@ namespace Phonon + /** + * This signal is emitted as soon as the total time of the media file is + * known or has changed. For most non-local media data the total +- * time of the media can only be known after some time. Initially the ++ * time of the media can only be known after some time. At that time the + * totalTime function can not return useful information. You have + * to wait for this signal to know the real total time. + * +--- a/src/3rdparty/phonon/phonon/mediasource.cpp ++++ b/src/3rdparty/phonon/phonon/mediasource.cpp +@@ -140,12 +140,8 @@ MediaSourcePrivate::~MediaSourcePrivate( + { + #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (autoDelete) { +- //here we use deleteLater because this object +- //might be destroyed from another thread +- if (stream) +- stream->deleteLater(); +- if (ioDevice) +- ioDevice->deleteLater(); ++ delete stream; ++ delete ioDevice; + } + #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + } +--- a/src/3rdparty/phonon/phonon/objectdescription.cpp ++++ b/src/3rdparty/phonon/phonon/objectdescription.cpp +@@ -29,6 +29,7 @@ + #include <QtCore/QStringList> + #include "backendinterface.h" + #include "platformplugin.h" ++#include "pulsesupport_p.h" + + QT_BEGIN_NAMESPACE + +@@ -108,22 +109,24 @@ bool ObjectDescriptionData::isValid() co + + ObjectDescriptionData *ObjectDescriptionData::fromIndex(ObjectDescriptionType type, int index) + { ++ BackendInterface *iface = qobject_cast<BackendInterface *>(Factory::backend()); ++ + // prefer to get the ObjectDescriptionData from the platform plugin for audio devices + #ifndef QT_NO_PHONON_PLATFORMPLUGIN +- if (type == AudioOutputDeviceType || type == AudioCaptureDeviceType) { +- PlatformPlugin *platformPlugin = Factory::platformPlugin(); +- if (platformPlugin) { +- QList<int> indexes = platformPlugin->objectDescriptionIndexes(type); +- if (indexes.contains(index)) { +- QHash<QByteArray, QVariant> properties = platformPlugin->objectDescriptionProperties(type, index); +- return new ObjectDescriptionData(index, properties); ++ if (!iface || !PulseSupport::getInstance()->isActive()) { ++ if (type == AudioOutputDeviceType || type == AudioCaptureDeviceType) { ++ PlatformPlugin *platformPlugin = Factory::platformPlugin(); ++ if (platformPlugin) { ++ QList<int> indexes = platformPlugin->objectDescriptionIndexes(type); ++ if (indexes.contains(index)) { ++ QHash<QByteArray, QVariant> properties = platformPlugin->objectDescriptionProperties(type, index); ++ return new ObjectDescriptionData(index, properties); ++ } + } + } + } + #endif //QT_NO_PHONON_PLATFORMPLUGIN + +- QObject *b = Factory::backend(); +- BackendInterface *iface = qobject_cast<BackendInterface *>(b); + if (iface) { + QList<int> indexes = iface->objectDescriptionIndexes(type); + if (indexes.contains(index)) { +--- a/src/3rdparty/phonon/phonon/objectdescriptionmodel.cpp ++++ b/src/3rdparty/phonon/phonon/objectdescriptionmodel.cpp +@@ -67,8 +67,6 @@ static const char qt_meta_stringdata_Pho + namespace Phonon + { + +-#if !defined(Q_CC_MINGW) || __MINGW32_MAJOR_VERSION >= 4 +- + template<> const QMetaObject ObjectDescriptionModel<AudioOutputDeviceType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioOutputDeviceType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +@@ -139,7 +137,6 @@ int ObjectDescriptionModel<type>::qt_met + return QAbstractListModel::qt_metacall(_c, _id, _a); + } + */ +-#endif + + int ObjectDescriptionModelData::rowCount(const QModelIndex &parent) const + { +@@ -321,8 +318,8 @@ bool ObjectDescriptionModelData::dropMim + } + } + d->model->beginInsertRows(QModelIndex(), row, row + toInsert.size() - 1); +- for (int i = 0 ; i < toInsert.count(); ++i) { +- d->data.insert(row, toInsert.at(i)); ++ foreach (const QExplicitlySharedDataPointer<ObjectDescriptionData> &obj, toInsert) { ++ d->data.insert(row, obj); + } + d->model->endInsertRows(); + return true; +@@ -365,8 +362,7 @@ QStringList ObjectDescriptionModelData:: + return QStringList(QLatin1String("application/x-phonon-objectdescription") + QString::number(static_cast<int>(type))); + } + +-#if !defined(Q_CC_MINGW) || __MINGW32_MAJOR_VERSION >= 4 +-#if !defined(Q_CC_MSVC) || _MSC_VER > 1300 || defined(Q_CC_INTEL) ++#if !defined(Q_CC_MSVC) || _MSC_VER > 1300 || defined(Q_CC_INTEL) || defined(Q_CC_MINGW) + #define INSTANTIATE_META_FUNCTIONS(type) \ + template const QMetaObject *ObjectDescriptionModel<type>::metaObject() const; \ + template void *ObjectDescriptionModel<type>::qt_metacast(const char *) +@@ -384,7 +380,6 @@ INSTANTIATE_META_FUNCTIONS(VideoCodecTyp + INSTANTIATE_META_FUNCTIONS(ContainerFormatType); + INSTANTIATE_META_FUNCTIONS(VisualizationType); + */ +-#endif //Q_CC_MINGW + } // namespace Phonon + + #endif //QT_NO_PHONON_OBJECTDESCRIPTIONMODEL +--- a/src/3rdparty/phonon/phonon/objectdescriptionmodel.h ++++ b/src/3rdparty/phonon/phonon/objectdescriptionmodel.h +@@ -35,6 +35,18 @@ QT_BEGIN_NAMESPACE + + #ifndef QT_NO_PHONON_OBJECTDESCRIPTIONMODEL + ++/* MinGW 3.4.x gives an ICE when trying to instantiate one of the ++ ObjectDescriptionModel<foo> classes because it can't handle ++ half exported classes correct. gcc 4.3.x has a fix for this but ++ we currently there's no official gcc 4.3 on windows available. ++ Because of this we need this little hack ++ */ ++#if defined(Q_CC_MINGW) ++#define PHONON_EXPORT_ODM ++#else ++#define PHONON_EXPORT_ODM PHONON_EXPORT ++#endif ++ + namespace Phonon + { + class ObjectDescriptionModelDataPrivate; +@@ -139,21 +151,6 @@ namespace Phonon + ObjectDescriptionModelDataPrivate *const d; + }; + +-/* Required to ensure template class vtables are exported on both symbian +-and existing builds. */ +-#if defined(Q_OS_SYMBIAN) && defined(Q_CC_RVCT) +-// RVCT compiler (2.2.686) requires the export declaration to be on the class to export vtables +-// MWC compiler works both ways +-// GCCE compiler is unknown (it can't compile QtCore yet) +-#define PHONON_TEMPLATE_CLASS_EXPORT PHONON_EXPORT +-#define PHONON_TEMPLATE_CLASS_MEMBER_EXPORT +-#else +-// Windows builds (at least) do not support export declaration on templated class +-// But if you export a member function, the vtable is implicitly exported +-#define PHONON_TEMPLATE_CLASS_EXPORT +-#define PHONON_TEMPLATE_CLASS_MEMBER_EXPORT PHONON_EXPORT +-#endif +- + /** \class ObjectDescriptionModel objectdescriptionmodel.h Phonon/ObjectDescriptionModel + * \short The ObjectDescriptionModel class provides a model from + * a list of ObjectDescription objects. +@@ -190,26 +187,17 @@ and existing builds. */ + * \author Matthias Kretz <kretz@kde.org> + */ + template<ObjectDescriptionType type> +- class PHONON_TEMPLATE_CLASS_EXPORT ObjectDescriptionModel : public QAbstractListModel ++ class ObjectDescriptionModel : public QAbstractListModel + { + public: + Q_OBJECT_CHECK +- +-/* MinGW 3.4.x gives an ICE when trying to instantiate one of the +- ObjectDescriptionModel<foo> classes because it can't handle +- half exported classes correct. gcc 4.3.x has a fix for this but +- we currently there's no official gcc 4.3 on windows available. +- Because of this we need this little hack +- */ +-#if !defined(Q_CC_MINGW) || __MINGW32_MAJOR_VERSION >= 4 + /** \internal */ +- static PHONON_TEMPLATE_CLASS_MEMBER_EXPORT const QMetaObject staticMetaObject; ++ static PHONON_EXPORT const QMetaObject staticMetaObject; + /** \internal */ +- PHONON_TEMPLATE_CLASS_MEMBER_EXPORT const QMetaObject *metaObject() const; ++ PHONON_EXPORT_ODM const QMetaObject *metaObject() const; + /** \internal */ +- PHONON_TEMPLATE_CLASS_MEMBER_EXPORT void *qt_metacast(const char *_clname); ++ PHONON_EXPORT_ODM void *qt_metacast(const char *_clname); + //int qt_metacall(QMetaObject::Call _c, int _id, void **_a); +-#endif + + /** + * Returns the number of rows in the model. This value corresponds +@@ -307,8 +295,8 @@ and existing builds. */ + */ + inline void setModelData(const QList<ObjectDescription<type> > &data) { //krazy:exclude=inline + QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > list; +- for (int i = 0; i < data.count(); ++i) { +- list += data.at(i).d; ++ Q_FOREACH (const ObjectDescription<type> &desc, data) { ++ list << desc.d; + } + d->setModelData(list); + } +@@ -322,8 +310,8 @@ and existing builds. */ + inline QList<ObjectDescription<type> > modelData() const { //krazy:exclude=inline + QList<ObjectDescription<type> > ret; + QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > list = d->modelData(); +- for (int i = 0; i < list.count(); ++i) { +- ret << ObjectDescription<type>(list.at(i)); ++ Q_FOREACH (const QExplicitlySharedDataPointer<ObjectDescriptionData> &data, list) { ++ ret << ObjectDescription<type>(data); + } + return ret; + } +--- a/src/3rdparty/phonon/phonon/path.cpp ++++ b/src/3rdparty/phonon/phonon/path.cpp +@@ -58,8 +58,8 @@ class ConnectionTransaction + PathPrivate::~PathPrivate() + { + #ifndef QT_NO_PHONON_EFFECT +- for (int i = 0; i < effects.count(); ++i) { +- effects.at(i)->k_ptr->removeDestructionHandler(this); ++ foreach (Effect *e, effects) { ++ e->k_ptr->removeDestructionHandler(this); + } + delete effectsParent; + #endif +@@ -233,8 +233,8 @@ bool Path::disconnect() + if (d->sourceNode) + list << d->sourceNode->k_ptr->backendObject(); + #ifndef QT_NO_PHONON_EFFECT +- for (int i = 0; i < d->effects.count(); ++i) { +- list << d->effects.at(i)->k_ptr->backendObject(); ++ foreach(Effect *e, d->effects) { ++ list << e->k_ptr->backendObject(); + } + #endif + if (d->sinkNode) { +@@ -260,8 +260,8 @@ bool Path::disconnect() + d->sourceNode = 0; + + #ifndef QT_NO_PHONON_EFFECT +- for (int i = 0; i < d->effects.count(); ++i) { +- d->effects.at(i)->k_ptr->removeDestructionHandler(d.data()); ++ foreach(Effect *e, d->effects) { ++ e->k_ptr->removeDestructionHandler(d.data()); + } + d->effects.clear(); + #endif +@@ -292,13 +292,11 @@ MediaNode *Path::sink() const + bool PathPrivate::executeTransaction( const QList<QObjectPair> &disconnections, const QList<QObjectPair> &connections) + { + QSet<QObject*> nodesForTransaction; +- for (int i = 0; i < disconnections.count(); ++i) { +- const QObjectPair &pair = disconnections.at(i); ++ foreach(const QObjectPair &pair, disconnections) { + nodesForTransaction << pair.first; + nodesForTransaction << pair.second; + } +- for (int i = 0; i < connections.count(); ++i) { +- const QObjectPair &pair = connections.at(i); ++ foreach(const QObjectPair &pair, connections) { + nodesForTransaction << pair.first; + nodesForTransaction << pair.second; + } +@@ -310,8 +308,8 @@ bool PathPrivate::executeTransaction( co + if (!transaction) + return false; + +- QList<QObjectPair>::const_iterator it = disconnections.constBegin(); +- for(;it != disconnections.constEnd();++it) { ++ QList<QObjectPair>::const_iterator it = disconnections.begin(); ++ for(;it != disconnections.end();++it) { + const QObjectPair &pair = *it; + if (!backend->disconnectNodes(pair.first, pair.second)) { + +@@ -327,8 +325,8 @@ bool PathPrivate::executeTransaction( co + } + } + +- for(it = connections.constBegin(); it != connections.constEnd(); ++it) { +- const QObjectPair pair = *it; ++ for(it = connections.begin(); it != connections.end();++it) { ++ const QObjectPair &pair = *it; + if (!backend->connectNodes(pair.first, pair.second)) { + //Error: a connection failed + QList<QObjectPair>::const_iterator it2 = connections.begin(); +@@ -340,8 +338,7 @@ bool PathPrivate::executeTransaction( co + } + + //and now let's reconnect the nodes that were disconnected: rollback +- for (int i = 0; i < disconnections.count(); ++i) { +- const QObjectPair &pair = disconnections.at(i); ++ foreach(const QObjectPair &pair, disconnections) { + bool success = backend->connectNodes(pair.first, pair.second); + Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection + Q_UNUSED(success); +@@ -420,8 +417,7 @@ void PathPrivate::phononObjectDestroyed( + sinkNode = 0; + } else { + #ifndef QT_NO_PHONON_EFFECT +- for (int i = 0; i < effects.count(); ++i) { +- Effect *e = effects.at(i); ++ foreach (Effect *e, effects) { + if (e->k_ptr == mediaNodePrivate) { + removeEffect(e); + } +--- a/src/3rdparty/phonon/phonon/phonondefs.h ++++ b/src/3rdparty/phonon/phonon/phonondefs.h +@@ -29,6 +29,11 @@ + QT_BEGIN_HEADER + QT_BEGIN_NAMESPACE + ++#ifdef PHONON_BACKEND_VERSION_4_4 ++# ifndef PHONON_BACKEND_VERSION_4_3 ++# define PHONON_BACKEND_VERSION_4_3 ++# endif ++#endif + #ifdef PHONON_BACKEND_VERSION_4_3 + # ifndef PHONON_BACKEND_VERSION_4_2 + # define PHONON_BACKEND_VERSION_4_2 +--- a/src/3rdparty/phonon/phonon/phononnamespace.h ++++ /dev/null +@@ -1,311 +0,0 @@ +-/* This file is part of the KDE project +- Copyright (C) 2005-2008 Matthias Kretz <kretz@kde.org> +- +- This library is free software; you can redistribute it and/or +- modify it under the terms of the GNU Lesser General Public +- License as published by the Free Software Foundation; either +- version 2.1 of the License, or (at your option) version 3, or any +- later version accepted by the membership of KDE e.V. (or its +- successor approved by the membership of KDE e.V.), Nokia Corporation +- (or its successors, if any) and the KDE Free Qt Foundation, which shall +- act as a proxy defined in Section 6 of version 3 of the license. +- +- This library is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- Lesser General Public License for more details. +- +- You should have received a copy of the GNU Lesser General Public +- License along with this library. If not, see <http://www.gnu.org/licenses/>. +- +-*/ +- +-#ifndef PHONONNAMESPACE_H +-#define PHONONNAMESPACE_H +- +-#include "phonon_export.h" +- +-#ifdef __QT_SYNCQT__ +-// Tell syncqt to create a "Global" header here +-#pragma qt_class(Phonon::Global) +-#endif +- +-/** +- * Helper macro that can be used like +- * \code +- * #if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 4, 0)) +- * \endcode +- */ +-#define PHONON_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) +- +-/** +- * PHONON_VERSION is (major << 16) + (minor << 8) + patch. +- */ +-#define PHONON_VERSION PHONON_VERSION_CHECK(4, 3, 1) +- +-/** +- * PHONON_VERSION_STR is "major.minor.patch". E.g. "4.2.1" +- */ +-#define PHONON_VERSION_STR "4.3.1" +- +-QT_BEGIN_HEADER +-QT_BEGIN_NAMESPACE +- +-class QString; +- +-/** +- * \brief The %KDE Multimedia classes +- * +- * In this Namespace you find the classes to access Multimedia functions for +- * audio and video playback. Those classes are not dependent +- * on any specific framework (like they were in pre KDE4 times) but rather use +- * exchangeable backends to do the work. +- * +- * If you want to write a new backend take a look at \ref phonon_backend_development_page. +- * +- * \author Matthias Kretz <kretz@kde.org> +- */ +-namespace Phonon +-{ +- PHONON_EXPORT const char *phononVersion(); +- +- /** +- * Enum to identify the media discs supported by MediaObject. +- * +- * \see MediaSource(Phonon::DiscType, const QString &deviceName) +- */ +- enum DiscType { +- /** +- * No disc was selected. This is only useful as a return value from +- * MediaSource::distType(); +- */ +- NoDisc = -1, +- /** +- * Identifies Audio CDs. +- */ +- Cd = 0, +- /** +- * Identifies DVDs (not arbitrary data DVDs, only movie DVDs). +- */ +- Dvd = 1, +- /** +- * Identifies Video CDs. +- */ +- Vcd = 2 +- }; +- +- /** +- * Provided as keys for \ref MediaObject::metaData for convenience, in addition to the strings defined in +- * the Ogg Vorbis specification. +- */ +- enum MetaData { +- /** +- * The artist generally considered responsible for the work. In popular +- * music this is usually the performing band or singer. For classical +- * music it would be the composer. For an audio book it would be the +- * author of the original text. +- */ +- ArtistMetaData, +- /** +- * The collection name to which this track belongs. +- */ +- AlbumMetaData, +- /** +- * Track/Work name +- */ +- TitleMetaData, +- /** +- * Date the track was recorded +- */ +- DateMetaData, +- /** +- * A short text indication of music genre +- */ +- GenreMetaData, +- /** +- * The track number of this piece if part of a specific larger +- * collection or album +- */ +- TracknumberMetaData, +- /** +- * A short text description of the contents +- */ +- DescriptionMetaData, +- MusicBrainzDiscIdMetaData +- }; +- +- /** +- * The state the media producing object is in at the moment. +- * +- * \see MediaObject +- */ +- enum State +- { +- /** +- * After construction it might take a while before the Player is +- * ready to play(). Normally this doesn't happen for local +- * files, but can happen for remote files where the asynchronous +- * mimetype detection and prebuffering can take a while. +- */ +- LoadingState, +- /** +- * The Player has a valid media file loaded and is ready for +- * playing. +- */ +- StoppedState, +- /** +- * The Player is playing a media file. +- */ +- PlayingState, +- /** +- * The Player is waiting for data to be able to continue +- * playing. +- */ +- BufferingState, +- /** +- * The Player is currently paused. +- */ +- PausedState, +- /** +- * An unrecoverable error occurred. The Object is unusable in this state. +- */ +- ErrorState +- }; +- +- /** +- * Set's the category your program should be listed in in the mixer. +- * +- * A Jukebox will set this to Music, a VoIP program to Communication, a +- * DVD player to video, and so on. +- * +- * \note These categories can also become useful for an application that +- * controls the volumes automatically, like turning down the music when a call +- * comes in, or turning down the notifications when the media player knows +- * it's playing classical music. +- * +- * \see AudioOutput::setCategory +- */ +- enum Category +- { +- /** +- * Will make use of the default device. +- */ +- NoCategory = -1, +- /** +- * If the sounds produced are notifications (bing, beep and such) you +- * should use this category. +- */ +- NotificationCategory = 0, +- /** +- * If your application is a music player (like a jukebox or media player +- * playing an audio file). +- */ +- MusicCategory = 1, +- /** +- * If the sound is the audio channel of a video. +- */ +- VideoCategory = 2, +- /** +- * If your applications produces sounds from communication with somebody +- * else (VoIP, voice chat). +- */ +- CommunicationCategory = 3, +- /** +- * Sound produced by a computer game should go into this category. +- */ +- GameCategory = 4, +- /** +- * Sounds produced for accessibility (e.g. Text-To-Speech) +- */ +- AccessibilityCategory = 5, +- /** +- * \internal +- * Holds the largest value of categories. +- */ +- LastCategory = AccessibilityCategory +- }; +- +- /** +- * Tells your program how to recover from an error. +- * +- * \see MediaObject::errorType() +- */ +- enum ErrorType { +- /** +- * No error. MediaObject::errorType() returns this if +- * MediaObject::state() != Phonon::ErrorState. +- */ +- NoError = 0, +- /** +- * Playback should work, and trying with another URL should work. +- */ +- NormalError = 1, +- /** +- * Something important does not work. Your program cannot continue +- * playback or capture or whatever it was trying to do +- * without help from the user. +- */ +- FatalError = 2 +- }; +- +- /** +- * Returns a (translated) string to show to the user identifying the given +- * Category. +- */ +- PHONON_EXPORT QString categoryToString(Category c); +- +- // TODO: naming +- /*enum MediaStreamType { +- Audio = 1, +- Video = 2, +- StillImage = 4, +- Subtitle = 8, +- AllMedia = 0xFFFFFFFF +- }; +- Q_DECLARE_FLAGS(MediaStreamTypes, MediaStreamType)*/ +-} // namespace Phonon +-//Q_DECLARE_OPERATORS_FOR_FLAGS(Phonon::MediaStreamTypes) +- +-QT_END_NAMESPACE +- +-//X class kdbgstream; +-//X #include <kdebug.h> +-//X /** +-//X * Implements needed operator to use Phonon::State with kDebug +-//X */ +-//X inline PHONON_EXPORT kdbgstream &operator<<(kdbgstream & stream, const Phonon::State state) +-//X { +-//X switch(state) +-//X { +-//X case Phonon::ErrorState: +-//X stream << "ErrorState"; +-//X break; +-//X case Phonon::LoadingState: +-//X stream << "LoadingState"; +-//X break; +-//X case Phonon::StoppedState: +-//X stream << "StoppedState"; +-//X break; +-//X case Phonon::PlayingState: +-//X stream << "PlayingState"; +-//X break; +-//X case Phonon::BufferingState: +-//X stream << "BufferingState"; +-//X break; +-//X case Phonon::PausedState: +-//X stream << "PausedState"; +-//X break; +-//X } +-//X return stream; +-//X } +- +-#include <QtCore/QMetaType> +- +-Q_DECLARE_METATYPE(Phonon::State) +-Q_DECLARE_METATYPE(Phonon::ErrorType) +-Q_DECLARE_METATYPE(Phonon::Category) +- +-QT_END_HEADER +- +-// vim: sw=4 ts=4 tw=80 +-#endif // PHONONNAMESPACE_H +--- /dev/null ++++ b/phonon/pulsesupport.cpp +@@ -0,0 +1,968 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2009 Colin Guthrie <cguthrie@mandriva.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#include <QtCore/QtGlobal> ++#include <QtCore/QEventLoop> ++#include <QtCore/QDebug> ++#include <QtCore/QStringList> ++ ++#ifdef HAVE_PULSEAUDIO ++#include <glib.h> ++#include <pulse/pulseaudio.h> ++#include <pulse/xmalloc.h> ++#include <pulse/glib-mainloop.h> ++#include <pulse/ext-device-manager.h> ++#endif // HAVE_PULSEAUDIO ++ ++#include "pulsesupport_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++namespace Phonon ++{ ++ ++static PulseSupport* s_instance = NULL; ++ ++#ifdef HAVE_PULSEAUDIO ++/*** ++* Prints a conditional debug message based on the current debug level ++* If obj is provided, classname and objectname will be printed as well ++* ++* see debugLevel() ++*/ ++ ++static int debugLevel() { ++ static int level = -1; ++ if (level < 1) { ++ level = 0; ++ QString pulseenv = qgetenv("PHONON_PULSEAUDIO_DEBUG"); ++ int l = pulseenv.toInt(); ++ if (l > 0) ++ level = (l > 2 ? 2 : l); ++ } ++ return level; ++} ++ ++static void logMessage(const QString &message, int priority = 2, QObject *obj=0); ++static void logMessage(const QString &message, int priority, QObject *obj) ++{ ++ if (debugLevel() > 0) { ++ QString output; ++ if (obj) { ++ // Strip away namespace from className ++ QString className(obj->metaObject()->className()); ++ int nameLength = className.length() - className.lastIndexOf(':') - 1; ++ className = className.right(nameLength); ++ output.sprintf("%s %s (%s %p)", message.toLatin1().constData(), ++ obj->objectName().toLatin1().constData(), ++ className.toLatin1().constData(), obj); ++ } ++ else { ++ output = message; ++ } ++ if (priority <= debugLevel()) { ++ qDebug() << QString("PulseSupport(%1): %2").arg(priority).arg(output); ++ } ++ } ++} ++ ++ ++class AudioDevice ++{ ++ public: ++ inline ++ AudioDevice(QString name, QString desc, QString icon, uint32_t index) ++ : pulseName(name), pulseIndex(index) ++ { ++ properties["name"] = desc; ++ properties["description"] = ""; // We don't have descriptions (well we do, but we use them as the name!) ++ properties["icon"] = icon; ++ properties["available"] = (index != PA_INVALID_INDEX); ++ properties["isAdvanced"] = false; // Nothing is advanced! ++ } ++ ++ // Needed for QMap ++ inline AudioDevice() {} ++ ++ QString pulseName; ++ uint32_t pulseIndex; ++ QHash<QByteArray, QVariant> properties; ++}; ++bool operator!=(const AudioDevice &a, const AudioDevice &b) ++{ ++ return !(a.pulseName == b.pulseName && a.properties == b.properties); ++} ++ ++class PulseUserData ++{ ++ public: ++ inline ++ PulseUserData() ++ { ++ } ++ ++ QMap<QString, AudioDevice> newOutputDevices; ++ QMap<Phonon::Category, QMap<int, int> > newOutputDevicePriorities; // prio, device ++ ++ QMap<QString, AudioDevice> newCaptureDevices; ++ QMap<Phonon::Category, QMap<int, int> > newCaptureDevicePriorities; // prio, device ++}; ++ ++static QMap<QString, Phonon::Category> s_roleCategoryMap; ++ ++static bool s_pulseActive = false; ++ ++static pa_glib_mainloop *s_mainloop = NULL; ++static pa_context *s_context = NULL; ++static QEventLoop *s_connectionEventloop = NULL; ++ ++ ++ ++static int s_deviceIndexCounter = 0; ++ ++static QMap<QString, int> s_outputDeviceIndexes; ++static QMap<int, AudioDevice> s_outputDevices; ++static QMap<Phonon::Category, QMap<int, int> > s_outputDevicePriorities; // prio, device ++static QMap<QString, uint32_t> s_outputStreamIndexMap; ++static QMap<QString, int> s_outputStreamMoveQueue; ++ ++static QMap<QString, int> s_captureDeviceIndexes; ++static QMap<int, AudioDevice> s_captureDevices; ++static QMap<Phonon::Category, QMap<int, int> > s_captureDevicePriorities; // prio, device ++static QMap<QString, uint32_t> s_captureStreamIndexMap; ++static QMap<QString, int> s_captureStreamMoveQueue; ++ ++ ++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) { ++ 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); ++ } ++ ++ if (eol < 0) { ++ logMessage(QString("Failed to initialize device manager extension: %1").arg(pa_strerror(pa_context_errno(c)))); ++ // OK so we don't have the device manager extension, but we can show a single device and fake it. ++ int index; ++ s_outputDeviceIndexes.clear(); ++ s_outputDevices.clear(); ++ s_outputDevicePriorities.clear(); ++ index = s_deviceIndexCounter++; ++ s_outputDeviceIndexes.insert("sink:default", index); ++ s_outputDevices.insert(index, AudioDevice("sink:default", QObject::tr("PulseAudio Sound Server").toUtf8(), "audio-backend-pulseaudio", 0)); ++ for (int i = Phonon::NoCategory; i <= Phonon::LastCategory; ++i) { ++ Phonon::Category cat = static_cast<Phonon::Category>(i); ++ s_outputDevicePriorities[cat].insert(0, index); ++ } ++ ++ s_captureDeviceIndexes.clear(); ++ s_captureDevices.clear(); ++ s_captureDevicePriorities.clear(); ++ index = s_deviceIndexCounter++; ++ s_captureDeviceIndexes.insert("source:default", index); ++ s_captureDevices.insert(index, AudioDevice("source:default", QObject::tr("PulseAudio Sound Server").toUtf8(), "audio-backend-pulseaudio", 0)); ++ for (int i = Phonon::NoCategory; i <= Phonon::LastCategory; ++i) { ++ Phonon::Category cat = static_cast<Phonon::Category>(i); ++ s_captureDevicePriorities[cat].insert(0, index); ++ } ++ ++ 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. ++ ++ QMap<QString, AudioDevice>::iterator newdev_it; ++ ++ // Check for new output devices or things changing about known output devices. ++ bool output_changed = false; ++ for (newdev_it = u->newOutputDevices.begin(); newdev_it != u->newOutputDevices.end(); ++newdev_it) { ++ QString name = newdev_it.key(); ++ ++ // The name + index map is always written when a new device is added. ++ Q_ASSERT(s_outputDeviceIndexes.contains(name)); ++ ++ int index = s_outputDeviceIndexes[name]; ++ if (!s_outputDevices.contains(index)) { ++ // This is a totally new device ++ output_changed = true; ++ logMessage(QString("Brand New Output Device Found.")); ++ s_outputDevices.insert(index, *newdev_it); ++ } else if (s_outputDevices[index] != *newdev_it) { ++ // We have this device already, but is it different? ++ output_changed = true; ++ logMessage(QString("Change to Existing Output Device (may be Added/Removed or something else)")); ++ s_outputDevices.remove(index); ++ s_outputDevices.insert(index, *newdev_it); ++ } ++ } ++ // Go through the output devices we know about and see if any are no longer mentioned in the list. ++ QMutableMapIterator<QString, int> output_existing_it(s_outputDeviceIndexes); ++ while (output_existing_it.hasNext()) { ++ output_existing_it.next(); ++ if (!u->newOutputDevices.contains(output_existing_it.key())) { ++ output_changed = true; ++ logMessage(QString("Output Device Completely Removed")); ++ s_outputDevices.remove(output_existing_it.value()); ++ output_existing_it.remove(); ++ } ++ } ++ ++ // Check for new capture devices or things changing about known capture devices. ++ bool capture_changed = false; ++ for (newdev_it = u->newCaptureDevices.begin(); newdev_it != u->newCaptureDevices.end(); ++newdev_it) { ++ QString name = newdev_it.key(); ++ ++ // The name + index map is always written when a new device is added. ++ Q_ASSERT(s_captureDeviceIndexes.contains(name)); ++ ++ int index = s_captureDeviceIndexes[name]; ++ if (!s_captureDevices.contains(index)) { ++ // This is a totally new device ++ capture_changed = true; ++ logMessage(QString("Brand New Capture Device Found.")); ++ s_captureDevices.insert(index, *newdev_it); ++ } else if (s_captureDevices[index] != *newdev_it) { ++ // We have this device already, but is it different? ++ capture_changed = true; ++ logMessage(QString("Change to Existing Capture Device (may be Added/Removed or something else)")); ++ s_captureDevices.remove(index); ++ s_captureDevices.insert(index, *newdev_it); ++ } ++ } ++ // Go through the capture devices we know about and see if any are no longer mentioned in the list. ++ QMutableMapIterator<QString, int> capture_existing_it(s_captureDeviceIndexes); ++ while (capture_existing_it.hasNext()) { ++ capture_existing_it.next(); ++ if (!u->newCaptureDevices.contains(capture_existing_it.key())) { ++ capture_changed = true; ++ logMessage(QString("Capture Device Completely Removed")); ++ s_captureDevices.remove(capture_existing_it.value()); ++ capture_existing_it.remove(); ++ } ++ } ++ ++ // Just copy accross the new priority lists as we know they are valid ++ if (s_outputDevicePriorities != u->newOutputDevicePriorities) { ++ output_changed = true; ++ s_outputDevicePriorities = u->newOutputDevicePriorities; ++ } ++ if (s_captureDevicePriorities != u->newCaptureDevicePriorities) { ++ capture_changed = true; ++ s_captureDevicePriorities = u->newCaptureDevicePriorities; ++ } ++ ++ if (s_instance) { ++ if (output_changed) ++ s_instance->emitObjectDescriptionChanged(AudioOutputDeviceType); ++ if (capture_changed) ++ s_instance->emitObjectDescriptionChanged(AudioCaptureDeviceType); ++ } ++ ++ // We can free the user data as we will not be called again. ++ delete u; ++ ++ // Some debug ++ logMessage(QString("Output Device Priority List:")); ++ for (int i = Phonon::NoCategory; i <= Phonon::LastCategory; ++i) { ++ Phonon::Category cat = static_cast<Phonon::Category>(i); ++ if (s_outputDevicePriorities.contains(cat)) { ++ logMessage(QString(" Phonon Category %1").arg(cat)); ++ int count = 0; ++ foreach (int j, s_outputDevicePriorities[cat]) { ++ QHash<QByteArray, QVariant> &props = s_outputDevices[j].properties; ++ logMessage(QString(" %1. %2 (Available: %3)").arg(++count).arg(props["name"].toString()).arg(props["available"].toBool())); ++ } ++ } ++ } ++ logMessage(QString("Capture Device Priority List:")); ++ for (int i = Phonon::NoCategory; i <= Phonon::LastCategory; ++i) { ++ Phonon::Category cat = static_cast<Phonon::Category>(i); ++ if (s_captureDevicePriorities.contains(cat)) { ++ logMessage(QString(" Phonon Category %1").arg(cat)); ++ int count = 0; ++ foreach (int j, s_captureDevicePriorities[cat]) { ++ QHash<QByteArray, QVariant> &props = s_captureDevices[j].properties; ++ logMessage(QString(" %1. %2 (Available: %3)").arg(++count).arg(props["name"].toString()).arg(props["available"].toBool())); ++ } ++ } ++ } ++ } ++ ++ if (!info) ++ return; ++ ++ Q_ASSERT(info->name); ++ Q_ASSERT(info->description); ++ Q_ASSERT(info->icon); ++ ++ // QString wrapper ++ QString name(info->name); ++ int index; ++ QMap<Phonon::Category, QMap<int, int> > *new_prio_map_cats; // prio, device ++ QMap<QString, AudioDevice> *new_devices; ++ ++ if (name.startsWith("sink:")) { ++ new_devices = &u->newOutputDevices; ++ new_prio_map_cats = &u->newOutputDevicePriorities; ++ ++ if (s_outputDeviceIndexes.contains(name)) ++ index = s_outputDeviceIndexes[name]; ++ else ++ index = s_outputDeviceIndexes[name] = s_deviceIndexCounter++; ++ } else if (name.startsWith("source:")) { ++ new_devices = &u->newCaptureDevices; ++ new_prio_map_cats = &u->newCaptureDevicePriorities; ++ ++ if (s_captureDeviceIndexes.contains(name)) ++ index = s_captureDeviceIndexes[name]; ++ else ++ index = s_captureDeviceIndexes[name] = s_deviceIndexCounter++; ++ } else { ++ // This indicates a bug in pulseaudio. ++ return; ++ } ++ ++ // Add the new device itself. ++ new_devices->insert(name, AudioDevice(name, info->description, info->icon, info->index)); ++ ++ // For each role in the priority, map it to a phonon category and store the order. ++ for (uint32_t i = 0; i < info->n_role_priorities; ++i) { ++ pa_ext_device_manager_role_priority_info* role_prio = &info->role_priorities[i]; ++ Q_ASSERT(role_prio->role); ++ ++ if (s_roleCategoryMap.contains(role_prio->role)) { ++ Phonon::Category cat = s_roleCategoryMap[role_prio->role]; ++ ++ (*new_prio_map_cats)[cat].insert(role_prio->priority, index); ++ } ++ } ++} ++ ++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); ++ ++ if (eol < 0) { ++ if (pa_context_errno(c) == PA_ERR_NOENTITY) ++ return; ++ ++ logMessage(QString("Sink input callback failure")); ++ return; ++ } ++ ++ if (eol > 0) ++ return; ++ ++ Q_ASSERT(i); ++ ++ // loop through (*i) and extract phonon->streamindex... ++ const char *t; ++ 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)); ++ } ++} ++ ++void source_output_cb(pa_context *c, const pa_source_output_info *i, int eol, void *userdata) { ++ Q_UNUSED(userdata); ++ Q_ASSERT(c); ++ ++ if (eol < 0) { ++ if (pa_context_errno(c) == PA_ERR_NOENTITY) ++ return; ++ ++ logMessage(QString("Source output callback failure")); ++ return; ++ } ++ ++ if (eol > 0) ++ return; ++ ++ Q_ASSERT(i); ++ ++ // loop through (*i) and extract phonon->streamindex... ++ const char *t; ++ 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)); ++ } ++} ++ ++static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) { ++ Q_UNUSED(userdata); ++ ++ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { ++ case PA_SUBSCRIPTION_EVENT_SINK_INPUT: ++ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { ++ QString phononid = s_outputStreamIndexMap.key(index); ++ if (!phononid.isEmpty()) { ++ if (s_outputStreamIndexMap.contains(phononid)) { ++ logMessage(QString("Phonon Output Stream %1 is gone at the PA end. Marking it as invalid in our cache as we may reuse it.").arg(phononid)); ++ s_outputStreamIndexMap[phononid] = PA_INVALID_INDEX; ++ } else { ++ logMessage(QString("Removing Phonon Output Stream %1 (it's gone!)").arg(phononid)); ++ s_outputStreamIndexMap.remove(phononid); ++ s_outputStreamMoveQueue.remove(phononid); ++ } ++ } ++ } else { ++ pa_operation *o; ++ if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, NULL))) { ++ logMessage(QString("pa_context_get_sink_input_info() failed")); ++ return; ++ } ++ pa_operation_unref(o); ++ } ++ break; ++ ++ case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: ++ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { ++ QString phononid = s_captureStreamIndexMap.key(index); ++ if (!phononid.isEmpty()) { ++ if (s_captureStreamIndexMap.contains(phononid)) { ++ logMessage(QString("Phonon Capture Stream %1 is gone at the PA end. Marking it as invalid in our cache as we may reuse it.").arg(phononid)); ++ s_captureStreamIndexMap[phononid] = PA_INVALID_INDEX; ++ } else { ++ logMessage(QString("Removing Phonon Capture Stream %1 (it's gone!)").arg(phononid)); ++ s_captureStreamIndexMap.remove(phononid); ++ s_captureStreamMoveQueue.remove(phononid); ++ } ++ } ++ } else { ++ pa_operation *o; ++ if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, NULL))) { ++ logMessage(QString("pa_context_get_sink_input_info() failed")); ++ return; ++ } ++ pa_operation_unref(o); ++ } ++ break; ++ } ++} ++ ++ ++static void ext_device_manager_subscribe_cb(pa_context *c, void *) { ++ Q_ASSERT(c); ++ ++ pa_operation *o; ++ 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) { ++ s_connectionEventloop->exit(0); ++ s_connectionEventloop = NULL; ++ } ++ logMessage(QString("pa_ext_device_manager_read() failed")); ++ return; ++ } ++ pa_operation_unref(o); ++ ++ ++ // 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 void context_state_callback(pa_context *c, void *) ++{ ++ Q_ASSERT(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; ++ ++ case PA_CONTEXT_READY: ++ // Attempt to load things up ++ ext_device_manager_subscribe_cb(c, NULL); ++ break; ++ ++ case PA_CONTEXT_FAILED: ++ s_pulseActive = false; ++ if (s_connectionEventloop) { ++ s_connectionEventloop->exit(0); ++ s_connectionEventloop = NULL; ++ } ++ break; ++ ++ case PA_CONTEXT_TERMINATED: ++ default: ++ s_pulseActive = false; ++ /// @todo Deal with reconnection... ++ break; ++ } ++} ++#endif // HAVE_PULSEAUDIO ++ ++ ++PulseSupport* PulseSupport::getInstance() ++{ ++ if (NULL == s_instance) { ++ s_instance = new PulseSupport(); ++ } ++ return s_instance; ++} ++ ++void PulseSupport::shutdown() ++{ ++ if (NULL != s_instance) { ++ delete s_instance; ++ s_instance = NULL; ++ } ++} ++ ++PulseSupport::PulseSupport() ++ : QObject() ++{ ++#ifdef HAVE_PULSEAUDIO ++ // Initialise our map (is there a better way to do this?) ++ s_roleCategoryMap["none"] = Phonon::NoCategory; ++ s_roleCategoryMap["video"] = Phonon::VideoCategory; ++ s_roleCategoryMap["music"] = Phonon::MusicCategory; ++ s_roleCategoryMap["game"] = Phonon::GameCategory; ++ s_roleCategoryMap["event"] = Phonon::NotificationCategory; ++ s_roleCategoryMap["phone"] = Phonon::CommunicationCategory; ++ //s_roleCategoryMap["animation"]; // No Mapping ++ //s_roleCategoryMap["production"]; // No Mapping ++ s_roleCategoryMap["a11y"] = Phonon::AccessibilityCategory; ++ ++ // To allow for easy debugging, give an easy way to disable this pulseaudio check ++ QString pulseenv = qgetenv("PHONON_PULSEAUDIO_DISABLE"); ++ if (pulseenv.toInt()) ++ return; ++ ++ 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... ++ s_connectionEventloop->exec(); ++ } ++#endif ++} ++ ++PulseSupport::~PulseSupport() ++{ ++#ifdef HAVE_PULSEAUDIO ++ if (s_context) { ++ pa_context_disconnect(s_context); ++ s_context = NULL; ++ } ++ ++ if (s_mainloop) { ++ pa_glib_mainloop_free(s_mainloop); ++ s_mainloop = NULL; ++ } ++ ++ if (s_connectionEventloop) { ++ delete s_connectionEventloop; ++ s_connectionEventloop = NULL; ++ } ++#endif ++} ++ ++bool PulseSupport::isActive() ++{ ++#ifdef HAVE_PULSEAUDIO ++ return s_pulseActive; ++#else ++ return false; ++#endif ++} ++ ++void PulseSupport::disable() ++{ ++#ifdef HAVE_PULSEAUDIO ++ s_pulseActive = false; ++#endif ++} ++ ++QList<int> PulseSupport::objectDescriptionIndexes(ObjectDescriptionType type) const ++{ ++ QList<int> list; ++ ++ if (type != AudioOutputDeviceType && type != AudioCaptureDeviceType) ++ return list; ++ ++#ifdef HAVE_PULSEAUDIO ++ if (s_pulseActive) { ++ switch (type) { ++ ++ case AudioOutputDeviceType: { ++ QMap<QString, int>::iterator it; ++ for (it = s_outputDeviceIndexes.begin(); it != s_outputDeviceIndexes.end(); ++it) { ++ list.append(*it); ++ } ++ break; ++ } ++ case AudioCaptureDeviceType: { ++ QMap<QString, int>::iterator it; ++ for (it = s_captureDeviceIndexes.begin(); it != s_captureDeviceIndexes.end(); ++it) { ++ list.append(*it); ++ } ++ break; ++ } ++ default: ++ break; ++ } ++ } ++#endif ++ ++ return list; ++} ++ ++QHash<QByteArray, QVariant> PulseSupport::objectDescriptionProperties(ObjectDescriptionType type, int index) const ++{ ++ QHash<QByteArray, QVariant> ret; ++ ++ if (type != AudioOutputDeviceType && type != AudioCaptureDeviceType) ++ return ret; ++ ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(index); ++#else ++ if (s_pulseActive) { ++ switch (type) { ++ ++ case AudioOutputDeviceType: ++ Q_ASSERT(s_outputDevices.contains(index)); ++ ret = s_outputDevices[index].properties; ++ break; ++ ++ case AudioCaptureDeviceType: ++ Q_ASSERT(s_captureDevices.contains(index)); ++ ret = s_captureDevices[index].properties; ++ break; ++ ++ default: ++ break; ++ } ++ } ++#endif ++ ++ return ret; ++} ++ ++QList<int> PulseSupport::objectIndexesByCategory(ObjectDescriptionType type, Category category) const ++{ ++ QList<int> ret; ++ ++ if (type != AudioOutputDeviceType && type != AudioCaptureDeviceType) ++ return ret; ++ ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(category); ++#else ++ if (s_pulseActive) { ++ switch (type) { ++ ++ case AudioOutputDeviceType: ++ if (s_outputDevicePriorities.contains(category)) ++ ret = s_outputDevicePriorities[category].values(); ++ break; ++ ++ case AudioCaptureDeviceType: ++ if (s_captureDevicePriorities.contains(category)) ++ ret = s_captureDevicePriorities[category].values(); ++ break; ++ ++ default: ++ break; ++ } ++ } ++#endif ++ ++ return ret; ++} ++ ++#ifdef HAVE_PULSEAUDIO ++static void setDevicePriority(Category category, QStringList list) ++{ ++ QString role = s_roleCategoryMap.key(category); ++ if (role.isEmpty()) ++ return; ++ ++ logMessage(QString("Reindexing %1: %2").arg(role).arg(list.join(", "))); ++ ++ char **devices; ++ devices = pa_xnew(char *, list.size()+1); ++ int i = 0; ++ foreach (QString str, list) { ++ devices[i++] = pa_xstrdup(str.toUtf8().constData()); ++ } ++ devices[list.size()] = NULL; ++ ++ pa_operation *o; ++ if (!(o = pa_ext_device_manager_reorder_devices_for_role(s_context, role.toUtf8().constData(), (const char**)devices, NULL, NULL))) ++ logMessage(QString("pa_ext_device_manager_reorder_devices_for_role() failed")); ++ else ++ pa_operation_unref(o); ++ ++ for (i = 0; i < list.size(); ++i) ++ pa_xfree(devices[i]); ++ pa_xfree(devices); ++} ++#endif ++ ++void PulseSupport::setOutputDevicePriorityForCategory(Category category, QList<int> order) ++{ ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(category); ++ Q_UNUSED(order); ++#else ++ QStringList list; ++ QList<int>::iterator it; ++ ++ for (it = order.begin(); it != order.end(); ++it) { ++ if (s_outputDevices.contains(*it)) { ++ list << s_outputDeviceIndexes.key(*it); ++ } ++ } ++ setDevicePriority(category, list); ++#endif ++} ++ ++void PulseSupport::setCaptureDevicePriorityForCategory(Category category, QList<int> order) ++{ ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(category); ++ Q_UNUSED(order); ++#else ++ QStringList list; ++ QList<int>::iterator it; ++ ++ for (it = order.begin(); it != order.end(); ++it) { ++ if (s_captureDevices.contains(*it)) { ++ list << s_captureDeviceIndexes.key(*it); ++ } ++ } ++ setDevicePriority(category, list); ++#endif ++} ++ ++void PulseSupport::setStreamPropList(Category category, QString streamUuid) ++{ ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(category); ++ Q_UNUSED(streamUuid); ++#else ++ QString role = s_roleCategoryMap.key(category); ++ if (role.isEmpty()) ++ return; ++ ++ logMessage(QString("Setting role to %1 for streamindex %2").arg(role).arg(streamUuid)); ++ setenv("PULSE_PROP_media.role", role.toLatin1().constData(), 1); ++ setenv("PULSE_PROP_phonon.streamid", streamUuid.toLatin1().constData(), 1); ++#endif ++} ++ ++void PulseSupport::emitObjectDescriptionChanged(ObjectDescriptionType type) ++{ ++ emit objectDescriptionChanged(type); ++} ++ ++bool PulseSupport::setOutputDevice(QString streamUuid, int device) { ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(streamUuid); ++ Q_UNUSED(device); ++ return false; ++#else ++ if (s_outputDevices.size() < 2) ++ return true; ++ ++ if (!s_outputDevices.contains(device)) { ++ logMessage(QString("Attempting to set Output Device for invalid device id %1.").arg(device)); ++ return false; ++ } ++ 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); ++ } else { ++ logMessage(QString("... Not found in map. Saving move for when the stream appears")); ++ } ++ return true; ++#endif ++} ++ ++bool PulseSupport::setCaptureDevice(QString streamUuid, int device) { ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(streamUuid); ++ Q_UNUSED(device); ++ return false; ++#else ++ if (s_captureDevices.size() < 2) ++ return true; ++ ++ if (!s_captureDevices.contains(device)) { ++ logMessage(QString("Attempting to set Capture Device for invalid device id %1.").arg(device)); ++ return false; ++ } ++ 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); ++ } else { ++ logMessage(QString("... Not found in map. Saving move for when the stream appears")); ++ } ++ return true; ++#endif ++} ++ ++void PulseSupport::clearStreamCache(QString streamUuid) { ++#ifndef HAVE_PULSEAUDIO ++ Q_UNUSED(streamUuid); ++ return; ++#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 ++} ++ ++} // namespace Phonon ++ ++QT_END_NAMESPACE ++ ++#include "moc_pulsesupport_p.cpp" ++ ++// vim: sw=4 ts=4 +--- /dev/null ++++ b/phonon/pulsesupport_p.h +@@ -0,0 +1,74 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2009 Colin Guthrie <cguthrie@mandriva.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#ifndef PHONON_PULSESUPPORT_H ++#define PHONON_PULSESUPPORT_H ++ ++#include "phonon_export.h" ++#include "phononnamespace.h" ++#include "objectdescription.h" ++ ++#include <QtCore/QtGlobal> ++#include <QtCore/QSet> ++ ++QT_BEGIN_HEADER ++QT_BEGIN_NAMESPACE ++ ++namespace Phonon ++{ ++ class PHONON_EXPORT PulseSupport : public QObject ++ { ++ Q_OBJECT ++ public: ++ static PulseSupport* getInstance(); ++ static void shutdown(); ++ ++ bool isActive(); ++ void disable(); ++ ++ QList<int> objectDescriptionIndexes(ObjectDescriptionType type) const; ++ QHash<QByteArray, QVariant> objectDescriptionProperties(ObjectDescriptionType type, int index) const; ++ QList<int> objectIndexesByCategory(ObjectDescriptionType type, Category category) const; ++ ++ void setOutputDevicePriorityForCategory(Category category, QList<int> order); ++ void setCaptureDevicePriorityForCategory(Category category, QList<int> order); ++ ++ void setStreamPropList(Category category, QString streamUuid); ++ void emitObjectDescriptionChanged(ObjectDescriptionType); ++ ++ bool setOutputDevice(QString streamUuid, int device); ++ bool setCaptureDevice(QString streamUuid, int device); ++ void clearStreamCache(QString streamUuid); ++ ++ signals: ++ void objectDescriptionChanged(ObjectDescriptionType); ++ private: ++ PulseSupport(); ++ ~PulseSupport(); ++ }; ++} // namespace Phonon ++ ++QT_END_NAMESPACE ++QT_END_HEADER ++ ++#endif // PHONON_PULSESUPPORT_H ++ +\ No newline at end of file +--- a/src/3rdparty/phonon/phonon/qsettingsgroup_p.h ++++ b/src/3rdparty/phonon/phonon/qsettingsgroup_p.h +@@ -27,8 +27,6 @@ + #include <QtCore/QString> + #include <QtCore/QVariant> + +-#ifndef QT_NO_PHONON_SETTINGSGROUP +- + QT_BEGIN_HEADER + QT_BEGIN_NAMESPACE + +@@ -89,6 +87,5 @@ class QSettingsGroup + + QT_END_NAMESPACE + QT_END_HEADER +-#endif //QT_NO_PHONON_SETTINGSGROUP + + #endif // PHONON_QSETTINGSGROUP_P_H +--- a/src/3rdparty/phonon/phonon/seekslider.cpp ++++ b/src/3rdparty/phonon/phonon/seekslider.cpp +@@ -72,12 +72,12 @@ void SeekSlider::setMediaObject(MediaObj + d->media = media; + + if (media) { +- connect(media, SIGNAL(stateChanged(Phonon::State,Phonon::State)), ++ connect(media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), + SLOT(_k_stateChanged(Phonon::State))); + connect(media, SIGNAL(totalTimeChanged(qint64)), SLOT(_k_length(qint64))); + connect(media, SIGNAL(tick(qint64)), SLOT(_k_tick(qint64))); + connect(media, SIGNAL(seekableChanged(bool)), SLOT(_k_seekableChanged(bool))); +- connect(media, SIGNAL(currentSourceChanged(Phonon::MediaSource)), SLOT(_k_currentSourceChanged())); ++ connect(media, SIGNAL(currentSourceChanged(const Phonon::MediaSource&)), SLOT(_k_currentSourceChanged())); + d->_k_stateChanged(media->state()); + d->_k_seekableChanged(media->isSeekable()); + d->_k_length(media->totalTime()); +--- a/src/3rdparty/phonon/phonon/seekslider_p.h ++++ b/src/3rdparty/phonon/phonon/seekslider_p.h +@@ -24,8 +24,8 @@ + #define SEEKSLIDER_P_H + + #include "seekslider.h" ++#include "swiftslider_p.h" + #include <QtGui/QBoxLayout> +-#include <QtGui/QSlider> + #include <QtGui/QLabel> + #include <QtGui/QPixmap> + #include <QtGui/QIcon> +@@ -84,7 +84,7 @@ class SeekSliderPrivate + void _k_currentSourceChanged(); + + QBoxLayout layout; +- QSlider slider; ++ SwiftSlider slider; + QLabel iconLabel; + QPointer<MediaObject> media; + bool ticking; +--- /dev/null ++++ b/phonon/swiftslider.cpp +@@ -0,0 +1,103 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2006-2008 Ricardo Villalba <rvm@escomposlinux.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#include "swiftslider_p.h" ++ ++#include <QtGui/QMouseEvent> ++#include <QtGui/QStyle> ++#include <QtGui/QStyleOption> ++ ++QT_BEGIN_NAMESPACE ++ ++#if !defined(QT_NO_PHONON_SEEKSLIDER) && !defined(QT_NO_PHONON_VOLUMESLIDER) ++ ++namespace Phonon ++{ ++ ++SwiftSlider::SwiftSlider(Qt::Orientation orientation, QWidget * parent) ++ : QSlider(orientation, parent) ++{ ++} ++ ++SwiftSlider::~SwiftSlider() ++{ ++} ++ ++// Function copied from qslider.cpp ++inline int SwiftSlider::pick(const QPoint &pt) const ++{ ++ return orientation() == Qt::Horizontal ? pt.x() : pt.y(); ++} ++ ++// Function copied from qslider.cpp and modified to make it compile ++int SwiftSlider::pixelPosToRangeValue(int pos) const ++{ ++ QStyleOptionSlider opt; ++ initStyleOption(&opt); ++ QRect gr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this); ++ QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); ++ int sliderMin, sliderMax, sliderLength; ++ ++ if (orientation() == Qt::Horizontal) { ++ sliderLength = sr.width(); ++ sliderMin = gr.x(); ++ sliderMax = gr.right() - sliderLength + 1; ++ } else { ++ sliderLength = sr.height(); ++ sliderMin = gr.y(); ++ sliderMax = gr.bottom() - sliderLength + 1; ++ } ++ return QStyle::sliderValueFromPosition(minimum(), maximum(), pos - sliderMin, ++ sliderMax - sliderMin, opt.upsideDown); ++} ++ ++// Based on code from qslider.cpp ++void SwiftSlider::mousePressEvent(QMouseEvent *event) ++{ ++ if (event->button() == Qt::LeftButton) { ++ QStyleOptionSlider opt; ++ initStyleOption(&opt); ++ const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); ++ const QPoint center = sliderRect.center() - sliderRect.topLeft(); ++ // to take half of the slider off for the setSliderPosition call we use the center - topLeft ++ ++ if (!sliderRect.contains(event->pos())) { ++ event->accept(); ++ ++ setSliderPosition(pixelPosToRangeValue(pick(event->pos() - center))); ++ triggerAction(SliderMove); ++ setRepeatAction(SliderNoAction); ++ } else { ++ QSlider::mousePressEvent(event); ++ } ++ } else { ++ QSlider::mousePressEvent(event); ++ } ++} ++ ++} // namespace Phonon ++ ++#endif //QT_NO_PHONON_VOLUMESLIDER && QT_NO_PHONON_VOLUMESLIDER ++ ++QT_END_NAMESPACE ++ ++#include "moc_swiftslider_p.cpp" +--- /dev/null ++++ b/phonon/swiftslider_p.h +@@ -0,0 +1,68 @@ ++/* This file is part of the KDE project ++ Copyright (C) 2006-2008 Ricardo Villalba <rvm@escomposlinux.org> ++ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) version 3, or any ++ later version accepted by the membership of KDE e.V. (or its ++ successor approved by the membership of KDE e.V.), Nokia Corporation ++ (or its successors, if any) and the KDE Free Qt Foundation, which shall ++ act as a proxy defined in Section 6 of version 3 of the license. ++ ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library. If not, see <http://www.gnu.org/licenses/>. ++ ++*/ ++ ++#ifndef SWIFTSLIDER_H ++#define SWIFTSLIDER_H ++ ++#include <QtGui/QSlider> ++ ++QT_BEGIN_NAMESPACE ++ ++#if !defined(QT_NO_PHONON_SEEKSLIDER) && !defined(QT_NO_PHONON_VOLUMESLIDER) ++ ++namespace Phonon ++{ ++ ++/** \class SwiftSlider swiftslider_p.h Phonon/SwiftSlider ++ * \short Modified QSlider that allows sudden/quick moves instead of stepped moves ("Click'n'Go" QSlider) ++ * ++ * This is an internal class used by SeekSlider and VolumeSlider. ++ * ++ * Ricardo Villalba, the original author of MySlider.cpp (from the SMPlayer project) ++ * gave his permission for the inclusion of this code inside Phonon by ++ * switching MySlider.cpp to the LGPLv2.1+ license. ++ * See http://smplayer.svn.sourceforge.net/viewvc/smplayer/smplayer/trunk/src/myslider.cpp?revision=2406&view=markup ++ * ++ * The original discussion about a "Click'n'Go QSlider": http://lists.trolltech.com/qt-interest/2006-11/msg00363.html ++ * ++ * \ingroup PhononWidgets ++ */ ++class SwiftSlider : public QSlider ++{ ++ Q_OBJECT ++public: ++ SwiftSlider(Qt::Orientation orientation, QWidget * parent); ++ ~SwiftSlider(); ++ ++private: ++ void mousePressEvent(QMouseEvent *event); ++ inline int pick(const QPoint &pt) const; ++ int pixelPosToRangeValue(int pos) const; ++}; ++ ++} // namespace Phonon ++ ++#endif //QT_NO_PHONON_VOLUMESLIDER && QT_NO_PHONON_VOLUMESLIDER ++ ++QT_END_NAMESPACE ++ ++#endif //SWIFTSLIDER_H +--- a/src/3rdparty/phonon/phonon/videowidget.cpp ++++ b/src/3rdparty/phonon/phonon/videowidget.cpp +@@ -28,8 +28,9 @@ + #include "phononnamespace_p.h" + + #include <QtGui/QAction> +- +-#define PHONON_INTERFACENAME VideoWidgetInterface ++#define IFACES4 VideoWidgetInterface44 ++#define IFACES0 VideoWidgetInterface, IFACES4 ++#define PHONON_INTERFACENAME IFACES0 + + QT_BEGIN_NAMESPACE + +@@ -48,6 +49,8 @@ VideoWidget::VideoWidget(QWidget *parent + setMouseTracking(true); + } + ++ ++ + VideoWidget::VideoWidget(VideoWidgetPrivate &dd, QWidget *parent) + : QWidget(parent), + Phonon::AbstractVideoOutput(dd) +@@ -98,6 +101,15 @@ PHONON_INTERFACE_SETTER(setHue, hue, qre + PHONON_INTERFACE_GETTER(qreal, saturation, d->saturation) + PHONON_INTERFACE_SETTER(setSaturation, saturation, qreal) + ++ ++QImage VideoWidget::snapshot() const { ++ K_D(const VideoWidget); ++ ConstIface<IFACES4> iface(d); ++ if(iface) return iface->snapshot(); ++ return QImage(); // TODO not implemented in VideoInterface ++} ++ ++ + void VideoWidget::setFullScreen(bool newFullScreen) + { + pDebug() << Q_FUNC_INFO << newFullScreen; +--- a/src/3rdparty/phonon/phonon/videowidget.h ++++ b/src/3rdparty/phonon/phonon/videowidget.h +@@ -172,6 +172,7 @@ class AbstractVideoOutput; + qreal contrast() const; + qreal hue() const; + qreal saturation() const; ++ QImage snapshot() const; + + //TODO: bar colors property + public Q_SLOTS: +--- a/src/3rdparty/phonon/phonon/videowidgetinterface.h ++++ b/src/3rdparty/phonon/phonon/videowidgetinterface.h +@@ -53,8 +53,21 @@ class VideoWidgetInterface + //X virtual int overlayCapabilities() const = 0; + //X virtual bool createOverlay(QWidget *widget, int type) = 0; + }; ++ ++class VideoWidgetInterface44 : public VideoWidgetInterface ++{ ++ public: ++ virtual QImage snapshot() const = 0; ++}; + } + ++#ifdef PHONON_BACKEND_VERSION_4_4 ++namespace Phonon { typedef VideoWidgetInterface44 VideoWidgetInterfaceLatest; } ++#else ++namespace Phonon { typedef VideoWidgetInterface VideoWidgetInterfaceLatest; } ++#endif ++ ++Q_DECLARE_INTERFACE(Phonon::VideoWidgetInterface44, "VideoWidgetInterface44.phonon.kde.org") + Q_DECLARE_INTERFACE(Phonon::VideoWidgetInterface, "VideoWidgetInterface3.phonon.kde.org") + + #endif //QT_NO_PHONON_VIDEO +--- a/src/3rdparty/phonon/phonon/volumeslider.cpp ++++ b/src/3rdparty/phonon/phonon/volumeslider.cpp +@@ -85,7 +85,7 @@ VolumeSlider::~VolumeSlider() + + bool VolumeSlider::isMuteVisible() const + { +- return !k_ptr->muteButton.isHidden(); ++ return k_ptr->muteButton.isVisible(); + } + + void VolumeSlider::setMuteVisible(bool visible) +--- a/src/3rdparty/phonon/phonon/volumeslider_p.h ++++ b/src/3rdparty/phonon/phonon/volumeslider_p.h +@@ -24,8 +24,8 @@ + #define VOLUMESLIDER_P_H + + #include "volumeslider.h" ++#include "swiftslider_p.h" + #include <QtGui/QBoxLayout> +-#include <QtGui/QSlider> + #include <QtGui/QLabel> + #include <QtGui/QPixmap> + #include <QtGui/QToolButton> +@@ -83,7 +83,7 @@ class VolumeSliderPrivate + + private: + QBoxLayout layout; +- QSlider slider; ++ SwiftSlider slider; + QToolButton muteButton; + QIcon volumeIcon; + QIcon mutedIcon; +--- a/src/3rdparty/phonon/waveout/mediaobject.cpp ++++ b/src/3rdparty/phonon/waveout/mediaobject.cpp +@@ -247,7 +247,7 @@ namespace Phonon + m_stopped(0) + { + m_thread = new WorkerThread(); +- connect(this, SIGNAL(outOfData(QIODevice*,QByteArray*,bool*)), m_thread, SLOT(stream(QIODevice*,QByteArray*,bool*))); ++ connect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*))); + m_thread->start(); + m_soundBuffer1.waveHeader = new WAVEHDR; + m_soundBuffer2.waveHeader = new WAVEHDR; +@@ -258,7 +258,7 @@ namespace Phonon + MediaObject::~MediaObject() + { + stop(); +- disconnect(this, SIGNAL(outOfData(QIODevice*,QByteArray*,bool*)), m_thread, SLOT(stream(QIODevice*,QByteArray*,bool*))); ++ disconnect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*))); + do { //The event loop of m_thread might not be started, yet + m_thread->quit(); //If the event loop is not started yet quit() does nothing + m_thread->wait(100); +@@ -281,8 +281,6 @@ namespace Phonon + + bool MediaObject::isSeekable() const + { +- if (!m_stream) +- return false; + return !m_stream->isSequential(); + } + diff --git a/debian/patches/series b/debian/patches/series index 2b09b66..1a3a4b7 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -38,3 +38,4 @@ 92_armel_gcc43_valist_compat.diff 95_sparc_platform_definition.diff 96_powerpc_no_gc_sections.diff +97_update_phonon_4.3.80
\ No newline at end of file |