summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/phonon/ds9
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:34:13 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:34:13 (GMT)
commit67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch)
tree1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/3rdparty/phonon/ds9
downloadQt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip
Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz
Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2
Long live Qt!
Diffstat (limited to 'src/3rdparty/phonon/ds9')
-rw-r--r--src/3rdparty/phonon/ds9/CMakeLists.txt53
-rw-r--r--src/3rdparty/phonon/ds9/ConfigureChecks.cmake44
-rw-r--r--src/3rdparty/phonon/ds9/abstractvideorenderer.cpp118
-rw-r--r--src/3rdparty/phonon/ds9/abstractvideorenderer.h73
-rw-r--r--src/3rdparty/phonon/ds9/audiooutput.cpp111
-rw-r--r--src/3rdparty/phonon/ds9/audiooutput.h68
-rw-r--r--src/3rdparty/phonon/ds9/backend.cpp343
-rw-r--r--src/3rdparty/phonon/ds9/backend.h83
-rw-r--r--src/3rdparty/phonon/ds9/backendnode.cpp115
-rw-r--r--src/3rdparty/phonon/ds9/backendnode.h73
-rw-r--r--src/3rdparty/phonon/ds9/compointer.h114
-rw-r--r--src/3rdparty/phonon/ds9/ds9.desktop51
-rw-r--r--src/3rdparty/phonon/ds9/effect.cpp153
-rw-r--r--src/3rdparty/phonon/ds9/effect.h59
-rw-r--r--src/3rdparty/phonon/ds9/fakesource.cpp166
-rw-r--r--src/3rdparty/phonon/ds9/fakesource.h54
-rw-r--r--src/3rdparty/phonon/ds9/iodevicereader.cpp228
-rw-r--r--src/3rdparty/phonon/ds9/iodevicereader.h57
-rw-r--r--src/3rdparty/phonon/ds9/lgpl-2.1.txt504
-rw-r--r--src/3rdparty/phonon/ds9/lgpl-3.txt165
-rw-r--r--src/3rdparty/phonon/ds9/mediagraph.cpp1099
-rw-r--r--src/3rdparty/phonon/ds9/mediagraph.h148
-rw-r--r--src/3rdparty/phonon/ds9/mediaobject.cpp1208
-rw-r--r--src/3rdparty/phonon/ds9/mediaobject.h313
-rw-r--r--src/3rdparty/phonon/ds9/phononds9_namespace.h33
-rw-r--r--src/3rdparty/phonon/ds9/qasyncreader.cpp198
-rw-r--r--src/3rdparty/phonon/ds9/qasyncreader.h77
-rw-r--r--src/3rdparty/phonon/ds9/qaudiocdreader.cpp332
-rw-r--r--src/3rdparty/phonon/ds9/qaudiocdreader.h58
-rw-r--r--src/3rdparty/phonon/ds9/qbasefilter.cpp831
-rw-r--r--src/3rdparty/phonon/ds9/qbasefilter.h136
-rw-r--r--src/3rdparty/phonon/ds9/qmeminputpin.cpp357
-rw-r--r--src/3rdparty/phonon/ds9/qmeminputpin.h82
-rw-r--r--src/3rdparty/phonon/ds9/qpin.cpp653
-rw-r--r--src/3rdparty/phonon/ds9/qpin.h121
-rw-r--r--src/3rdparty/phonon/ds9/videorenderer_soft.cpp1011
-rw-r--r--src/3rdparty/phonon/ds9/videorenderer_soft.h68
-rw-r--r--src/3rdparty/phonon/ds9/videorenderer_vmr9.cpp333
-rw-r--r--src/3rdparty/phonon/ds9/videorenderer_vmr9.h56
-rw-r--r--src/3rdparty/phonon/ds9/videowidget.cpp397
-rw-r--r--src/3rdparty/phonon/ds9/videowidget.h96
-rw-r--r--src/3rdparty/phonon/ds9/volumeeffect.cpp296
-rw-r--r--src/3rdparty/phonon/ds9/volumeeffect.h71
43 files changed, 10606 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/ds9/CMakeLists.txt b/src/3rdparty/phonon/ds9/CMakeLists.txt
new file mode 100644
index 0000000..1bb6f6f
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+#
+# 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 or 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/>.
+project(phonon-ds9)
+include(ConfigureChecks.cmake)
+
+if (BUILD_PHONON_DS9)
+ find_package(OPENGL REQUIRED)
+
+ include_directories($ENV{DXDSDK_DIR}/include $ENV{DXSDK_DIR})
+ set(phonon_ds9_SRCS
+ abstractvideorenderer.cpp
+ audiooutput.cpp
+ backend.cpp
+ backendnode.cpp
+ effect.cpp
+ fakesource.cpp
+ iodevicereader.cpp
+ mediagraph.cpp
+ mediaobject.cpp
+ videowidget.cpp
+ videorenderer_vmr9.cpp
+ videorenderer_soft.cpp
+ volumeeffect.cpp
+ qbasefilter.cpp
+ qpin.cpp
+ qasyncreader.cpp
+ qaudiocdreader.cpp
+ qmeminputpin.cpp
+ )
+
+ add_definitions(-DPHONON_MAKE_QT_ONLY_BACKEND -DUNICODE)
+ automoc4_add_library(phonon_ds9 SHARED ${phonon_ds9_SRCS})
+ set_target_properties(phonon_ds9 PROPERTIES PREFIX "")
+ target_link_libraries(phonon_ds9
+ ${PHONON_LIBS} ${QT_QTOPENGL_LIBRARY} ${OPENGL_gl_LIBRARY}
+ dxguid strmiids dmoguids msdmo ole32 oleaut32 uuid gdi32)
+ install(TARGETS phonon_ds9
+ RUNTIME DESTINATION ${BIN_INSTALL_DIR}/phonon_backend
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+ install(FILES ds9.desktop DESTINATION ${SERVICES_INSTALL_DIR}/phononbackends)
+endif (BUILD_PHONON_DS9)
diff --git a/src/3rdparty/phonon/ds9/ConfigureChecks.cmake b/src/3rdparty/phonon/ds9/ConfigureChecks.cmake
new file mode 100644
index 0000000..c13056f
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/ConfigureChecks.cmake
@@ -0,0 +1,44 @@
+# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+#
+# 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 or 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/>.
+
+# We must find:
+# $DXSDK_DIR/include/d3d9.h
+# $DXDSK_DIR/$LIB/dxguid.lib
+# vmr9.h
+# dshow.h
+# strmiids.lib
+# dmoguids.lib
+# msdmo.lib
+include(CheckCXXSourceCompiles)
+
+macro_push_required_vars()
+
+set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${CMAKE_INCLUDE_PATH} $ENV{DXSDK_DIR} $ENV{DXSDK_DIR}/include)
+set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dxguid strmiids dmoguids msdmo)
+
+CHECK_CXX_SOURCE_COMPILES(
+"#include <d3d9.h>
+#include <dshow.h>
+#include <strmif.h>
+#include <vmr9.h>
+
+int main() { }" BUILD_PHONON_DS9)
+
+macro_pop_required_vars()
+
+if (BUILD_PHONON_DS9)
+ message(STATUS "Found DirectShow 9 support: $ENV{DXSDK_DIR}")
+else (BUILD_PHONON_DS9)
+ message(STATUS "DirectShow 9 support NOT found")
+endif (BUILD_PHONON_DS9)
diff --git a/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp b/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp
new file mode 100644
index 0000000..e932e70
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp
@@ -0,0 +1,118 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "abstractvideorenderer.h"
+
+#include <QtGui/QApplication>
+#include <QtGui/QDesktopWidget>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_VIDEO
+
+namespace Phonon
+{
+ namespace DS9
+ {
+
+ AbstractVideoRenderer::AbstractVideoRenderer() :
+ m_dstX(0), m_dstY(0), m_dstWidth(0), m_dstHeight(0),
+ m_active(false)
+ {
+ }
+
+ AbstractVideoRenderer::~AbstractVideoRenderer()
+ {
+ }
+
+ Filter AbstractVideoRenderer::getFilter() const
+ {
+ return m_filter;
+ }
+
+ QSize AbstractVideoRenderer::sizeHint() const
+ {
+ QSize s = videoSize();
+ if (s.isNull()) {
+ s = QSize(640, 480).boundedTo( QApplication::desktop()->availableGeometry().size() );
+ }
+ return s;
+ }
+
+ void AbstractVideoRenderer::setActive(bool b)
+ {
+ m_active = b;
+ }
+
+ bool AbstractVideoRenderer::isActive() const
+ {
+ return m_active;
+ }
+
+ void AbstractVideoRenderer::internalNotifyResize(const QSize &size, const QSize &videoSize,
+ Phonon::VideoWidget::AspectRatio aspectRatio, Phonon::VideoWidget::ScaleMode scaleMode)
+ {
+ //update the video rects
+ qreal ratio = -1.;
+ const int videoWidth = videoSize.width(),
+ videoHeight = videoSize.height();
+
+ switch(aspectRatio)
+ {
+ case Phonon::VideoWidget::AspectRatioAuto:
+ {
+ //preserve the aspect ratio of the video
+ ratio = qreal(videoWidth) / qreal(videoHeight);
+ }
+ break;
+ case Phonon::VideoWidget::AspectRatio4_3:
+ ratio = qreal(4. / 3.);
+ break;
+ case Phonon::VideoWidget::AspectRatio16_9:
+ ratio = qreal(16. / 9.);
+ break;
+ case Phonon::VideoWidget::AspectRatioWidget:
+ default:
+ break;
+ }
+
+ const qreal realWidth = size.width(),
+ realHeight = size.height();
+
+ //reinitialization
+ m_dstWidth = size.width();
+ m_dstHeight = size.height();
+ m_dstX = m_dstY = 0;
+
+ if (ratio > 0) {
+ if (realWidth / realHeight > ratio && scaleMode == Phonon::VideoWidget::FitInView
+ || realWidth / realHeight < ratio && scaleMode == Phonon::VideoWidget::ScaleAndCrop) {
+ //the height is correct, let's change the width
+ m_dstWidth = qRound(realHeight * ratio);
+ m_dstX = qRound((realWidth - realHeight * ratio) / 2.);
+ } else {
+ m_dstHeight = qRound(realWidth / ratio);
+ m_dstY = qRound((realHeight - realWidth / ratio) / 2.);
+ }
+ }
+ }
+ }
+}
+
+#endif //QT_NO_PHONON_EFFECT
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/abstractvideorenderer.h b/src/3rdparty/phonon/ds9/abstractvideorenderer.h
new file mode 100644
index 0000000..25c660f
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/abstractvideorenderer.h
@@ -0,0 +1,73 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_ABSTRACTVIDEORENDERER_H
+#define PHONON_ABSTRACTVIDEORENDERER_H
+
+#include "backendnode.h"
+
+#include <phonon/videowidget.h>
+
+QT_BEGIN_NAMESPACE
+
+class QImage;
+
+#ifndef QT_NO_PHONON_VIDEO
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ //this is the interface used by the videorenderer from the VideoWidget class
+ class AbstractVideoRenderer
+ {
+ public:
+ virtual ~AbstractVideoRenderer();
+
+ virtual void repaintCurrentFrame(QWidget *target, const QRect &rect) = 0;
+ virtual void notifyResize(const QSize&, Phonon::VideoWidget::AspectRatio, Phonon::VideoWidget::ScaleMode) = 0;
+ virtual void applyMixerSettings(qreal brightness, qreal contrast, qreal m_hue, qreal saturation) = 0;
+
+ void setActive(bool);
+ bool isActive() const;
+
+ virtual bool isNative() const = 0;
+ virtual QImage snapshot() const = 0;
+
+ Filter getFilter() const;
+ QSize sizeHint() const;
+
+ protected:
+ virtual QSize videoSize() const = 0;
+
+ AbstractVideoRenderer();
+ void internalNotifyResize(const QSize &size, const QSize &videoSize,
+ Phonon::VideoWidget::AspectRatio aspectRatio, Phonon::VideoWidget::ScaleMode scaleMode);
+
+
+ Filter m_filter;
+ int m_dstX, m_dstY, m_dstWidth, m_dstHeight;
+ bool m_active;
+ };
+ }
+}
+
+#endif //QT_NO_PHONON_VIDEO
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/audiooutput.cpp b/src/3rdparty/phonon/ds9/audiooutput.cpp
new file mode 100644
index 0000000..fcc062c
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/audiooutput.cpp
@@ -0,0 +1,111 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "audiooutput.h"
+#include "mediaobject.h"
+
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ AudioOutput::AudioOutput(Backend *back, QObject *parent)
+ : BackendNode(parent), m_currentIndex(0), m_crossfadeProgress(1.),
+ m_device(-1), m_backend(back), m_volume(0.)
+ {
+ }
+
+ AudioOutput::~AudioOutput()
+ {
+ }
+
+ int AudioOutput::outputDevice() const
+ {
+ return m_device;
+ }
+
+ static const qreal log10over20 = qreal(0.1151292546497022842); // ln(10) / 20
+
+ void AudioOutput::setVolume(qreal newVolume)
+ {
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ ComPointer<IBasicAudio> audio(m_filters[i], IID_IBasicAudio);
+ if (audio) {
+ const qreal currentVolume = newVolume * (m_currentIndex == i ? m_crossfadeProgress : 1-m_crossfadeProgress);
+ const qreal newDbVolume = (qMax(0., 1.-::log(::pow(currentVolume, -log10over20)))-1.) * 10000;
+ audio->put_Volume(qRound(newDbVolume));
+ }
+ }
+
+ if (m_volume != newVolume) {
+ m_volume = newVolume;
+ emit volumeChanged(newVolume);
+ }
+ }
+
+ void AudioOutput::setCrossFadingProgress(short currentIndex, qreal progress)
+ {
+ m_crossfadeProgress = progress;
+ m_currentIndex = currentIndex;
+ setVolume(m_volume);
+ }
+
+ bool AudioOutput::setOutputDevice(const AudioOutputDevice & newDevice)
+ {
+ //stub implementation
+ return setOutputDevice(newDevice.index());
+ }
+
+ qreal AudioOutput::volume() const
+ {
+ return m_volume;
+ }
+
+ bool AudioOutput::setOutputDevice(int newDevice)
+ {
+ if (newDevice == m_device) {
+ return true;
+ }
+
+ //free the previous one if it was already set
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ const Filter &oldFilter = m_filters[i];
+
+ Filter newFilter = m_backend->getAudioOutputFilter(newDevice);
+
+ if (m_mediaObject && oldFilter && newFilter) {
+ m_mediaObject->switchFilters(i, oldFilter, newFilter);
+ }
+
+ m_filters[i] = newFilter;
+
+
+ }
+
+ m_device = newDevice;
+ setVolume(m_volume);
+ return true;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_audiooutput.cpp"
diff --git a/src/3rdparty/phonon/ds9/audiooutput.h b/src/3rdparty/phonon/ds9/audiooutput.h
new file mode 100644
index 0000000..c3bfc8b
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/audiooutput.h
@@ -0,0 +1,68 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_AUDIOOUTPUT_H
+#define PHONON_AUDIOOUTPUT_H
+
+#include "backendnode.h"
+#include <phonon/audiooutputinterface.h>
+
+#include "backend.h"
+
+struct IBaseFilter;
+struct IBasicAudio;
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class AudioOutput : public BackendNode, public Phonon::AudioOutputInterface
+ {
+ Q_OBJECT
+ Q_INTERFACES(Phonon::AudioOutputInterface)
+ public:
+ AudioOutput(Backend *back, QObject *parent);
+ ~AudioOutput();
+
+ // Attributes Getters:
+ qreal volume() const;
+ int outputDevice() const;
+ void setVolume(qreal newVolume);
+ bool setOutputDevice(int newDevice);
+ bool setOutputDevice(const AudioOutputDevice & newDevice);
+ void setCrossFadingProgress(short currentIndex, qreal progress);
+
+ Q_SIGNALS:
+ void volumeChanged(qreal newVolume);
+ void audioDeviceFailed();
+
+ private:
+ short m_currentIndex;
+ qreal m_crossfadeProgress;
+
+ int m_device;
+ Backend *m_backend;
+ qreal m_volume;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // PHONON_AUDIOOUTPUT_H
diff --git a/src/3rdparty/phonon/ds9/backend.cpp b/src/3rdparty/phonon/ds9/backend.cpp
new file mode 100644
index 0000000..245749a
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/backend.cpp
@@ -0,0 +1,343 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "backend.h"
+#include "backendnode.h"
+
+#include "audiooutput.h"
+#include "effect.h"
+#include "mediaobject.h"
+#include "videowidget.h"
+#include "volumeeffect.h"
+
+//windows specific (DirectX Media Object)
+#include <dmo.h>
+
+#include <QtCore/QSettings>
+#include <QtCore/QSet>
+#include <QtCore/QVariant>
+
+#include <QtCore/QtPlugin>
+
+QT_BEGIN_NAMESPACE
+
+Q_EXPORT_PLUGIN2(phonon_ds9, Phonon::DS9::Backend);
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ bool Backend::AudioMoniker::operator==(const AudioMoniker &other)
+ {
+ return other->IsEqual(*this) == S_OK;
+ }
+
+
+ Backend::Backend(QObject *parent, const QVariantList &)
+ : QObject(parent)
+ {
+ ::CoInitialize(0);
+
+ //registering meta types
+ qRegisterMetaType<HRESULT>("HRESULT");
+ qRegisterMetaType<Graph>("Graph");
+ }
+
+ Backend::~Backend()
+ {
+ m_audioOutputs.clear();
+ m_audioEffects.clear();
+ ::CoUninitialize();
+ }
+
+ QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
+ {
+ switch (c)
+ {
+ case MediaObjectClass:
+ return new MediaObject(parent);
+ case AudioOutputClass:
+ return new AudioOutput(this, parent);
+#ifndef QT_NO_PHONON_EFFECT
+ case EffectClass:
+ return new Effect(m_audioEffects[ args[0].toInt() ], parent);
+#endif //QT_NO_PHONON_EFFECT
+#ifndef QT_NO_PHONON_VIDEO
+ case VideoWidgetClass:
+ return new VideoWidget(qobject_cast<QWidget *>(parent));
+#endif //QT_NO_PHONON_VIDEO
+#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
+ case VolumeFaderEffectClass:
+ return new VolumeEffect(parent);
+#endif //QT_NO_PHONON_VOLUMEFADEREFFECT
+ default:
+ return 0;
+ }
+ }
+
+ bool Backend::supportsVideo() const
+ {
+#ifndef QT_NO_PHONON_VIDEO
+ return true;
+#else
+ return false;
+#endif //QT_NO_PHONON_VIDEO
+ }
+
+ QStringList Backend::availableMimeTypes() const
+ {
+ QStringList ret;
+ {
+ QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\mplayer2\\mime types"), QSettings::NativeFormat);
+ ret += settings.childGroups();
+ }
+ {
+ QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\wmplayer\\mime types"), QSettings::NativeFormat);
+ ret += settings.childGroups();
+ }
+
+ ret.removeDuplicates();
+ ret.replaceInStrings("\\", "/");
+ qSort(ret);
+ return ret;
+ }
+
+ Filter Backend::getAudioOutputFilter(int index) const
+ {
+ Filter ret;
+ if (index >= 0 && index < m_audioOutputs.count()) {
+ m_audioOutputs.at(index)->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(&ret));
+ } else {
+ //just return the default audio renderer (not directsound)
+ ret = Filter(CLSID_AudioRender, IID_IBaseFilter);
+ }
+ return ret;
+ }
+
+
+ QList<int> Backend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const
+ {
+ QList<int> ret;
+
+ switch(type)
+ {
+ case Phonon::AudioOutputDeviceType:
+ {
+#ifdef Q_OS_WINCE
+ ret << 0; // only one audio device with index 0
+#else
+ ComPointer<ICreateDevEnum> devEnum(CLSID_SystemDeviceEnum, IID_ICreateDevEnum);
+ if (!devEnum) {
+ return ret; //it is impossible to enumerate the devices
+ }
+ ComPointer<IEnumMoniker> enumMon;
+ HRESULT hr = devEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, enumMon.pparam(), 0);
+ if (FAILED(hr)) {
+ break;
+ }
+ AudioMoniker mon;
+
+ //let's reorder the devices so that directshound appears first
+ int nbds = 0; //number of directsound devices
+
+ while (S_OK == enumMon->Next(1, mon.pparam(), 0)) {
+ LPOLESTR str = 0;
+ mon->GetDisplayName(0,0,&str);
+ const QString name = QString::fromUtf16((unsigned short*)str);
+ ComPointer<IMalloc> alloc;
+ ::CoGetMalloc(1, alloc.pparam());
+ alloc->Free(str);
+
+ int insert_pos = 0;
+ if (!m_audioOutputs.contains(mon)) {
+ insert_pos = m_audioOutputs.count();
+ m_audioOutputs.append(mon);
+ } else {
+ insert_pos = m_audioOutputs.indexOf(mon);
+ }
+
+ if (name.contains(QLatin1String("DirectSound"))) {
+ ret.insert(nbds++, insert_pos);
+ } else {
+ ret.append(insert_pos);
+ }
+ }
+#endif
+ break;
+ }
+#ifndef QT_NO_PHONON_EFFECT
+ case Phonon::EffectType:
+ {
+ m_audioEffects.clear();
+ ComPointer<IEnumDMO> enumDMO;
+ HRESULT hr = ::DMOEnum(DMOCATEGORY_AUDIO_EFFECT, DMO_ENUMF_INCLUDE_KEYED, 0, 0, 0, 0, enumDMO.pparam());
+ if (SUCCEEDED(hr)) {
+ CLSID clsid;
+ while (S_OK == enumDMO->Next(1, &clsid, 0, 0)) {
+ ret += m_audioEffects.count();
+ m_audioEffects.append(clsid);
+ }
+ }
+ break;
+ }
+ break;
+#endif //QT_NO_PHONON_EFFECT
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const
+ {
+ QHash<QByteArray, QVariant> ret;
+ switch (type)
+ {
+ case Phonon::AudioOutputDeviceType:
+ {
+#ifdef Q_OS_WINCE
+ ret["name"] = QLatin1String("default audio device");
+#else
+ const AudioMoniker &mon = m_audioOutputs[index];
+ LPOLESTR str = 0;
+ HRESULT hr = mon->GetDisplayName(0,0, &str);
+ if (SUCCEEDED(hr)) {
+ QString name = QString::fromUtf16((unsigned short*)str);
+ ComPointer<IMalloc> alloc;
+ ::CoGetMalloc(1, alloc.pparam());
+ alloc->Free(str);
+ ret["name"] = name.mid(name.indexOf('\\') + 1);
+ }
+#endif
+ }
+ break;
+#ifndef QT_NO_PHONON_EFFECT
+ case Phonon::EffectType:
+ {
+ WCHAR name[80]; // 80 is clearly stated in the MSDN doc
+ HRESULT hr = ::DMOGetName(m_audioEffects[index], name);
+ if (SUCCEEDED(hr)) {
+ ret["name"] = QString::fromUtf16((unsigned short*)name);
+ }
+ }
+ break;
+#endif //QT_NO_PHONON_EFFECT
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ bool Backend::endConnectionChange(QSet<QObject *> objects)
+ {
+ //end of a transaction
+ for(QSet<QObject *>::const_iterator it = objects.begin(); it != objects.end(); ++it) {
+ if (BackendNode *node = qobject_cast<BackendNode*>(*it)) {
+ MediaObject *mo = node->mediaObject();
+ if (mo) {
+ switch(mo->transactionState)
+ {
+ case Phonon::ErrorState:
+ case Phonon::StoppedState:
+ case Phonon::LoadingState:
+ //nothing to do
+ break;
+ case Phonon::PausedState:
+ mo->transactionState = Phonon::StoppedState;
+ mo->pause();
+ break;
+ default:
+ mo->transactionState = Phonon::StoppedState;
+ mo->play();
+ break;
+ }
+
+ if (mo->state() == Phonon::ErrorState)
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ bool Backend::startConnectionChange(QSet<QObject *> objects)
+ {
+ //let's save the state of the graph (before we stop it)
+ for(QSet<QObject *>::const_iterator it = objects.begin(); it != objects.end(); ++it) {
+ if (BackendNode *node = qobject_cast<BackendNode*>(*it)) {
+ if (MediaObject *mo = node->mediaObject()) {
+ if (mo->state() != Phonon::StoppedState) {
+ mo->transactionState = mo->state();
+ mo->ensureStopped(); //we have to stop the graph..
+ if (mo->state() == Phonon::ErrorState)
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool Backend::connectNodes(QObject *_source, QObject *_sink)
+ {
+ BackendNode *source = qobject_cast<BackendNode*>(_source);
+ if (!source) {
+ return false;
+ }
+ BackendNode *sink = qobject_cast<BackendNode*>(_sink);
+ if (!sink) {
+ return false;
+ }
+
+ //setting the graph if needed
+ if (source->mediaObject() == 0 && sink->mediaObject() == 0) {
+ //error: no graph selected
+ return false;
+ } else if (source->mediaObject() && source->mediaObject() != sink->mediaObject()) {
+ //this' graph becomes the common one
+ source->mediaObject()->grabNode(sink);
+ } else if (source->mediaObject() == 0) {
+ //sink's graph becomes the common one
+ sink->mediaObject()->grabNode(source);
+ }
+
+ return source->mediaObject()->connectNodes(source, sink);
+ }
+
+ bool Backend::disconnectNodes(QObject *_source, QObject *_sink)
+ {
+ BackendNode *source = qobject_cast<BackendNode*>(_source);
+ if (!source) {
+ return false;
+ }
+ BackendNode *sink = qobject_cast<BackendNode*>(_sink);
+ if (!sink) {
+ return false;
+ }
+
+ return source->mediaObject() == 0 ||
+ source->mediaObject()->disconnectNodes(source, sink);
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_backend.cpp"
diff --git a/src/3rdparty/phonon/ds9/backend.h b/src/3rdparty/phonon/ds9/backend.h
new file mode 100644
index 0000000..ad638f2
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/backend.h
@@ -0,0 +1,83 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_BACKEND_H
+#define PHONON_BACKEND_H
+
+#include "phononds9_namespace.h"
+#include <phonon/backendinterface.h>
+#include <phonon/phononnamespace.h>
+
+#include <QtCore/QList>
+
+#include "compointer.h"
+#include "backendnode.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class AudioOutput;
+ class MediaObject;
+
+ typedef Phonon::ObjectDescriptionType ObjectDescriptionType;
+
+ class Backend : public QObject, public Phonon::BackendInterface
+ {
+ Q_OBJECT
+ Q_INTERFACES(Phonon::BackendInterface)
+ public:
+ Backend(QObject *parent = 0, const QVariantList & = QVariantList());
+ virtual ~Backend();
+
+ QObject *createObject(Phonon::BackendInterface::Class, QObject *parent, const QList<QVariant> &args);
+
+ bool supportsVideo() const;
+ QStringList availableMimeTypes() const;
+
+ QList<int> objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const;
+ QHash<QByteArray, QVariant> objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const;
+
+ bool connectNodes(QObject *, QObject *);
+ bool disconnectNodes(QObject *, QObject *);
+
+ //transaction management
+ bool startConnectionChange(QSet<QObject *>);
+ bool endConnectionChange(QSet<QObject *>);
+
+ Filter getAudioOutputFilter(int index) const;
+
+ Q_SIGNALS:
+ void objectDescriptionChanged(ObjectDescriptionType);
+
+ private:
+ class AudioMoniker : public ComPointer<IMoniker>
+ {
+ public:
+ bool operator==(const AudioMoniker &other);
+ };
+ mutable QVector<AudioMoniker> m_audioOutputs;
+ mutable QVector<CLSID> m_audioEffects;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // PHONON_BACKEND_H
diff --git a/src/3rdparty/phonon/ds9/backendnode.cpp b/src/3rdparty/phonon/ds9/backendnode.cpp
new file mode 100644
index 0000000..7e0b3cd
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/backendnode.cpp
@@ -0,0 +1,115 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "backendnode.h"
+#include "mediaobject.h"
+
+#include <ocidl.h> // ISpecifyPropertyPages
+#include <olectl.h> // OleCreatePropertyFrame
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ // Displays a property dialog for a filter (experimental but should be put into main
+ /*static void showPropertyDialog(const Filter &filter)
+ {
+ ComPointer<ISpecifyPropertyPages> prop(filter, IID_ISpecifyPropertyPages);
+ if (prop != 0) {
+ IUnknown *iunk[] = {filter};
+ // Show the page.
+ CAUUID caGUID;
+ prop->GetPages(&caGUID);
+ OleCreatePropertyFrame(
+ 0, // Parent window
+ 0, 0, // (Reserved)
+ 0, // Caption for the dialog box
+ 1, // Number of objects (just the filter)
+ iunk, // Array of object pointers.
+ caGUID.cElems, // Number of property pages
+ caGUID.pElems, // Array of property page CLSIDs
+ 0, // Locale identifier
+ 0, 0 // Reserved
+ );
+ }
+ }*/
+
+ //for now we have 2 graphs that do the same
+ BackendNode::BackendNode(QObject *parent) : QObject(parent), m_mediaObject(0)
+ {
+ }
+
+ BackendNode::~BackendNode()
+ {
+ }
+
+ void BackendNode::setMediaObject(MediaObject *mo)
+ {
+ if (m_mediaObject) {
+ disconnect(m_mediaObject, SIGNAL(destroyed()), this, SLOT(mediaObjectDestroyed()));
+ }
+ m_mediaObject = mo;
+ connect(mo, SIGNAL(destroyed()), SLOT(mediaObjectDestroyed()));
+ }
+
+ void BackendNode::mediaObjectDestroyed()
+ {
+ //remove the filter from its graph
+ FILTER_INFO info;
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ const Filter &filter = m_filters[i];
+ if (!filter)
+ continue;
+ filter->QueryFilterInfo(&info);
+ if (info.pGraph) {
+ HRESULT hr = info.pGraph->RemoveFilter(filter);
+ Q_ASSERT(SUCCEEDED(hr));
+ Q_UNUSED(hr);
+ info.pGraph->Release();
+ }
+ }
+ m_mediaObject = 0;
+ }
+
+ QList<InputPin> BackendNode::pins(const Filter &filter, PIN_DIRECTION wantedDirection)
+ {
+ QList<InputPin> ret;
+ if (filter) {
+ ComPointer<IEnumPins> enumPin;
+ HRESULT hr = filter->EnumPins(enumPin.pparam());
+ Q_UNUSED(hr);
+ Q_ASSERT( SUCCEEDED(hr));
+ InputPin pin;
+ while (enumPin->Next(1, pin.pparam(), 0) == S_OK) {
+ PIN_DIRECTION dir;
+ hr = pin->QueryDirection(&dir);
+ Q_ASSERT( SUCCEEDED(hr));
+ if (dir == wantedDirection) {
+ ret.append(pin);
+ }
+ }
+ }
+ return ret;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_backendnode.cpp"
diff --git a/src/3rdparty/phonon/ds9/backendnode.h b/src/3rdparty/phonon/ds9/backendnode.h
new file mode 100644
index 0000000..17bd3fb
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/backendnode.h
@@ -0,0 +1,73 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_BACKENDNODE_H
+#define PHONON_BACKENDNODE_H
+
+#include "phononds9_namespace.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QVector>
+
+#include "compointer.h"
+
+QT_BEGIN_NAMESPACE
+
+
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class MediaObject;
+ typedef ComPointer<IPin> InputPin;
+ typedef ComPointer<IPin> OutputPin;
+ typedef ComPointer<IBaseFilter> Filter;
+ typedef ComPointer<IGraphBuilder> Graph;
+
+ class BackendNode : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ BackendNode(QObject *parent);
+ virtual ~BackendNode();
+
+ MediaObject *mediaObject() const {return m_mediaObject;}
+
+ static QList<InputPin> pins(const Filter &, PIN_DIRECTION);
+
+ Filter filter(int index) const { return m_filters[index]; }
+ //add a pointer to the base Media Object (giving access to the graph and error management)
+ void setMediaObject(MediaObject *mo);
+
+ //called by the connections to tell the node that it's been connection to anothe one through its 'inpin' input port
+ virtual void connected(BackendNode *, const InputPin& inpin) {}
+
+ private Q_SLOTS:
+ void mediaObjectDestroyed();
+
+ protected:
+ Filter m_filters[FILTER_COUNT];
+ MediaObject *m_mediaObject;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/compointer.h b/src/3rdparty/phonon/ds9/compointer.h
new file mode 100644
index 0000000..180febf
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/compointer.h
@@ -0,0 +1,114 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_COMPOINTER_H
+#define PHONON_COMPOINTER_H
+
+#include <windows.h>
+#include <dshow.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ template<class T> class ComPointer
+ {
+ public:
+ explicit ComPointer(T *t = 0) : m_t(t)
+ {
+ }
+
+ explicit ComPointer( const IID &clsid, const IID &iid) : m_t(0)
+ {
+ ::CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, iid,
+ reinterpret_cast<void**>(&m_t));
+ }
+
+ explicit ComPointer(IUnknown *_unk, const GUID &guid) : m_t(0)
+ {
+ if (_unk) {
+ _unk->QueryInterface(guid, reinterpret_cast<void**>(&m_t));
+ }
+ }
+
+ ComPointer(const ComPointer<T> &other) : m_t(other.m_t)
+ {
+ if (m_t) {
+ m_t->AddRef();
+ }
+ }
+
+ ComPointer<T> &operator=(const ComPointer<T> &other)
+ {
+ if (other.m_t) {
+ other.m_t->AddRef();
+ }
+ if (m_t) {
+ m_t->Release();
+ }
+ m_t = other.m_t;
+ return *this;
+ }
+
+ T *operator->() const
+ {
+ return m_t;
+ }
+
+ operator T*() const
+ {
+ return m_t;
+ }
+
+ //the following method first reinitialize their value to avoid mem leaks
+ T ** pparam()
+ {
+ if (m_t) {
+ m_t->Release();
+ m_t = 0;
+ }
+ return &m_t;
+ }
+
+ bool operator==(const ComPointer<T> &other) const
+ {
+ return m_t == other.m_t;
+ }
+
+ bool operator!=(const ComPointer<T> &other) const
+ {
+ return m_t != other.m_t;
+ }
+
+ ~ComPointer()
+ {
+ if (m_t) {
+ m_t->Release();
+ }
+ }
+
+ private:
+ T *m_t;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/ds9.desktop b/src/3rdparty/phonon/ds9/ds9.desktop
new file mode 100644
index 0000000..370011e
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/ds9.desktop
@@ -0,0 +1,51 @@
+[Desktop Entry]
+Type=Service
+X-KDE-ServiceTypes=PhononBackend
+MimeType=application/x-annodex;video/quicktime;video/x-quicktime;audio/x-m4a;application/x-quicktimeplayer;video/mkv;video/msvideo;video/x-msvideo;video/x-flic;audio/x-aiff;audio/aiff;audio/x-pn-aiff;audio/x-realaudio;audio/basic;audio/x-basic;audio/x-pn-au;audio/x-8svx;audio/8svx;audio/x-16sv;audio/168sv;image/x-ilbm;image/ilbm;video/x-anim;video/anim;image/png;image/x-png;video/mng;video/x-mng;audio/x-ogg;audio/x-speex+ogg;application/ogg;application/ogg;audio/vnd.rn-realaudio;audio/x-pn-realaudio-plugin;audio/x-real-audio;application/vnd.rn-realmedia;video/mpeg;video/x-mpeg;audio/x-wav;audio/wav;audio/x-pn-wav;audio/x-pn-windows-acm;audio/mpeg2;audio/x-mpeg2;audio/mpeg3;audio/x-mpeg3;audio/mpeg;audio/x-mpeg;x-mpegurl;audio/x-mpegurl;audio/mp3;audio/mpeg;
+X-KDE-Library=phonon_ds9
+X-KDE-PhononBackendInfo-InterfaceVersion=1
+X-KDE-PhononBackendInfo-Version=0.1
+X-KDE-PhononBackendInfo-Website=http://www.trolltech.com/
+InitialPreference=15
+
+Name=DirectShow9
+Name[pa]=ਡਾਇਰੈਕਸ਼ੋ9
+Name[sk]=DirectShow 9
+Name[sl]=DirectShow 9
+Name[sr]=Директшоу‑9
+Name[sr@latin]=DirectShow‑9
+Name[sv]=Directshow 9
+Name[x-test]=xxDirectShow9xx
+
+Comment=Phonon DirectShow9 backend
+Comment[bg]=Phonon DirectShow9
+Comment[ca]=Dorsal DirectShow9 del Phonon
+Comment[da]=DirectShow9-backend til Phonon
+Comment[de]=Phonon-Treiber für DirectShow9
+Comment[el]=Σύστημα υποστήριξης DirectShow9 του Phonon
+Comment[es]=Motor DirectShow9 para Phonon
+Comment[et]=Phononi DirectShow9 taustaprogramm
+Comment[fr]=Système de gestion DirectShow9 pour Phonon
+Comment[ga]=Inneall DirectShow9 le haghaidh Phonon
+Comment[gl]=Infraestrutura de DirectShow9 para Phonon
+Comment[it]=Motore DirectShow9 di Phonon
+Comment[ja]=Phonon DirectShow9 バックエンド
+Comment[ko]=Phonon DirectShow9 백엔드
+Comment[lv]=Phonon DirectShow9 aizmugure
+Comment[nds]=Phonon-Hülpprogrmm DirectShow9
+Comment[nl]=DirectShow9-backend (Phonon)
+Comment[nn]=Phonon-motor for DirectShow9
+Comment[pa]=ਫੋਨੋਨ ਡਾਇਰੈਕਟਸ਼ੋ9 ਬੈਕਐਂਡ
+Comment[pl]=Obsługa DirectShow9 przez Phonon
+Comment[pt]=Infra-estrutura do DirectShow9 para o Phonon
+Comment[pt_BR]=Infraestrutura Phonon DirectShow9
+Comment[sk]=Phonon DirectShow 9 podsystém
+Comment[sl]=Phononova Hrbtenica DirectShow 9
+Comment[sr]=Директшоу‑9 као позадина Фонона
+Comment[sr@latin]=DirectShow‑9 kao pozadina Phonona
+Comment[sv]=Phonon Directshow 9-gränssnitt
+Comment[tr]=Phonon DirectShow9 arka ucu
+Comment[uk]=Сервер DirectShow9 для Phonon
+Comment[x-test]=xxPhonon DirectShow9 backendxx
+Comment[zh_CN]=Phonon DirectShow9 后端
+Comment[zh_TW]=Phonon DirectShow9 後端介面
diff --git a/src/3rdparty/phonon/ds9/effect.cpp b/src/3rdparty/phonon/ds9/effect.cpp
new file mode 100644
index 0000000..dc4ac3d
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/effect.cpp
@@ -0,0 +1,153 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "effect.h"
+#include <phonon/effectparameter.h>
+
+#include <medparam.h>
+#include <dmo.h>
+#include <dmodshow.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_EFFECT
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ Effect::Effect(CLSID effectClass, QObject *parent)
+ : BackendNode(parent)
+ {
+ //creation of the filter
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ Filter &filter = m_filters[i];
+ filter = Filter(CLSID_DMOWrapperFilter, IID_IBaseFilter);
+ Q_ASSERT(filter);
+ ComPointer<IDMOWrapperFilter> wrapper(filter, IID_IDMOWrapperFilter);
+ Q_ASSERT(wrapper);
+ wrapper->Init(effectClass, DMOCATEGORY_AUDIO_EFFECT);
+ }
+ }
+
+ Effect::Effect(QObject *parent) : BackendNode(parent)
+ {
+ //at this point the QVector of Filter should be filled
+ }
+
+ Effect::~Effect()
+ {
+ }
+
+ QList<Phonon::EffectParameter> Effect::parameters() const
+ {
+ QList<Phonon::EffectParameter> ret;
+ ComPointer<IMediaParamInfo> paramInfo(m_filters[0], IID_IMediaParamInfo);
+ if (!paramInfo) {
+ return ret;
+ }
+ DWORD paramCount = 0;
+ paramInfo->GetParamCount( &paramCount);
+
+ for(quint32 i = 0; i < paramCount; i++) {
+ MP_PARAMINFO info;
+ HRESULT hr = paramInfo->GetParamInfo(i, &info);
+ Q_ASSERT(SUCCEEDED(hr));
+ WCHAR *name = 0;
+ hr = paramInfo->GetParamText(i, &name);
+ Q_ASSERT(SUCCEEDED(hr));
+ QVariant def, min, max;
+
+ QVariantList values;
+
+ switch(info.mpType)
+ {
+ case MPT_ENUM:
+ {
+ WCHAR *current = name;
+ current += wcslen(current) + 1; //skip the name
+ current += wcslen(current) + 1; //skip the unit
+ for(; *current; current += wcslen(current) + 1) {
+ values.append( QString::fromUtf16((unsigned short*)current) );
+ }
+ }
+ //FALLTHROUGH
+ case MPT_INT:
+ def = int(info.mpdNeutralValue);
+ min = int(info.mpdMinValue);
+ max = int(info.mpdMaxValue);
+ break;
+ case MPT_FLOAT:
+ def = info.mpdNeutralValue;
+ min = info.mpdMinValue;
+ max = info.mpdMaxValue;
+ break;
+ case MPT_BOOL:
+ def = bool(info.mpdNeutralValue);
+ break;
+ case MPT_MAX:
+ //Reserved ms-help://MS.PSDKSVR2003R2.1033/directshow/htm/mp_typeenumeration.htm
+ break;
+ }
+
+ Phonon::EffectParameter::Hints hint = info.mopCaps == MP_CAPS_CURVE_INVSQUARE ?
+ Phonon::EffectParameter::LogarithmicHint : Phonon::EffectParameter::Hints(0);
+
+ const QString n = QString::fromUtf16((unsigned short*)name);
+ ret.append(Phonon::EffectParameter(i, n, hint, def, min, max, values));
+ ::CoTaskMemFree(name); //let's free the memory
+ }
+ return ret;
+ }
+
+ QVariant Effect::parameterValue(const Phonon::EffectParameter &p) const
+ {
+ QVariant ret;
+ ComPointer<IMediaParams> params(m_filters[0], IID_IMediaParams);
+ Q_ASSERT(params);
+ MP_DATA data;
+ HRESULT hr = params->GetParam(p.id(), &data);
+ if(SUCCEEDED(hr))
+ return data;
+ else
+ return QVariant();
+ }
+
+ void Effect::setParameterValue(const Phonon::EffectParameter &p, const QVariant &v)
+ {
+ if (v.isNull()) {
+ return;
+ }
+
+ for(int i=0; i < FILTER_COUNT ; ++i) {
+ const Filter &filter = m_filters[i];
+ ComPointer<IMediaParams> params(filter, IID_IMediaParams);
+ Q_ASSERT(params);
+
+ MP_DATA data = float(v.toDouble());
+ params->SetParam(p.id(), data);
+ }
+ }
+
+ }
+}
+
+#endif //QT_NO_PHONON_EFFECT
+
+QT_END_NAMESPACE
+
+#include "moc_effect.cpp"
diff --git a/src/3rdparty/phonon/ds9/effect.h b/src/3rdparty/phonon/ds9/effect.h
new file mode 100644
index 0000000..50f3ea2
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/effect.h
@@ -0,0 +1,59 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_AUDIOEFFECT_H
+#define PHONON_AUDIOEFFECT_H
+
+#include <QtCore/QObject>
+#include <phonon/effectinterface.h>
+#include "backendnode.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_EFFECT
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class EffectInterface;
+
+ class Effect : public BackendNode, public Phonon::EffectInterface
+ {
+ Q_OBJECT
+ Q_INTERFACES(Phonon::EffectInterface)
+ public:
+ Effect(CLSID effectClass, QObject *parent);
+ ~Effect();
+
+ QList<Phonon::EffectParameter> parameters() const;
+ QVariant parameterValue(const Phonon::EffectParameter &) const;
+ void setParameterValue(const Phonon::EffectParameter &, const QVariant &);
+
+
+ protected:
+ //this is the constructor called by the explicit subclasses of effect
+ Effect(QObject *parent);
+ };
+ }
+}
+
+#endif //QT_NO_PHONON_EFFECT
+
+QT_END_NAMESPACE
+
+#endif // PHONON_AUDIOEFFECT_H
diff --git a/src/3rdparty/phonon/ds9/fakesource.cpp b/src/3rdparty/phonon/ds9/fakesource.cpp
new file mode 100644
index 0000000..9a61a2e
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/fakesource.cpp
@@ -0,0 +1,166 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "fakesource.h"
+#include "qpin.h"
+
+#include <dshow.h>
+#include <initguid.h>
+#include <dvdmedia.h> // VIDEOINFOHEADER2
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ static WAVEFORMATEX g_defaultWaveFormat = {WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0};
+ static BITMAPINFOHEADER g_defautBitmapHeader = { sizeof(BITMAPINFOHEADER), 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
+ static VIDEOINFOHEADER2 g_defaultVideoInfo = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ class FakePin : public QPin
+ {
+ public:
+ FakePin(FakeSource *source, const AM_MEDIA_TYPE &mt) :
+ QPin(source, PINDIR_OUTPUT, QVector<AM_MEDIA_TYPE>() << mt), m_source(source)
+ {
+ setAvailable(true);
+ }
+
+ ~FakePin()
+ {
+ }
+
+
+ STDMETHODIMP Disconnect()
+ {
+ HRESULT hr = QPin::Disconnect();
+ if (SUCCEEDED(hr)) {
+ setAvailable(true);
+ }
+ return hr;
+ }
+
+
+ STDMETHODIMP Connect(IPin *pin, const AM_MEDIA_TYPE *type)
+ {
+ HRESULT hr = QPin::Connect(pin, type);
+ if (SUCCEEDED(hr)) {
+ setAvailable(false);
+ }
+ return hr;
+ }
+
+ private:
+ void setAvailable(bool avail)
+ {
+ if (mediaTypes().first().majortype == MEDIATYPE_Audio) {
+ if (avail) {
+ m_source->addAvailableAudioPin(this);
+ } else {
+ m_source->removeAvailableAudioPin(this);
+ }
+ } else {
+ if (avail) {
+ m_source->addAvailableVideoPin(this);
+ } else {
+ m_source->removeAvailableVideoPin(this);
+ }
+ }
+ }
+
+ FakeSource *m_source;
+
+
+ };
+
+ FakeSource::FakeSource() : QBaseFilter(CLSID_NULL)
+ {
+ createFakeAudioPin();
+ createFakeVideoPin();
+ }
+
+ FakeSource::~FakeSource()
+ {
+ }
+
+ void FakeSource::addAvailableAudioPin(FakePin *pin)
+ {
+ availableAudioPins += pin;
+ }
+
+ void FakeSource::addAvailableVideoPin(FakePin *pin)
+ {
+ availableVideoPins += pin;
+ }
+
+ void FakeSource::removeAvailableAudioPin(FakePin *pin)
+ {
+ availableAudioPins -= pin;
+
+ if (availableAudioPins.isEmpty()) {
+ createFakeAudioPin();
+ }
+ }
+
+ void FakeSource::removeAvailableVideoPin(FakePin *pin)
+ {
+ availableVideoPins -= pin;
+
+ if (availableVideoPins.isEmpty()) {
+ createFakeVideoPin();
+ }
+ }
+
+ void FakeSource::createFakeAudioPin()
+ {
+ AM_MEDIA_TYPE mt;
+ qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE));
+ mt.majortype = MEDIATYPE_Audio;
+ mt.subtype = MEDIASUBTYPE_PCM;
+ mt.formattype = FORMAT_WaveFormatEx;
+ mt.lSampleSize = 2;
+
+ //fake the format (stereo 44.1 khz stereo 16 bits)
+ mt.cbFormat = sizeof(WAVEFORMATEX);
+ mt.pbFormat = reinterpret_cast<BYTE*>(&g_defaultWaveFormat);
+
+ new FakePin(this, mt);
+ }
+
+ void FakeSource::createFakeVideoPin()
+ {
+ AM_MEDIA_TYPE mt;
+ qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE));
+ mt.majortype = MEDIATYPE_Video;
+ mt.subtype = MEDIASUBTYPE_RGB32;
+ mt.formattype = FORMAT_VideoInfo2;
+ mt.bFixedSizeSamples = 1;
+
+ g_defaultVideoInfo.bmiHeader = g_defautBitmapHeader;
+
+ //fake the format
+ mt.cbFormat = sizeof(VIDEOINFOHEADER2);
+ mt.pbFormat = reinterpret_cast<BYTE*>(&g_defaultVideoInfo);
+
+ new FakePin(this, mt);
+ }
+
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/fakesource.h b/src/3rdparty/phonon/ds9/fakesource.h
new file mode 100644
index 0000000..a32e0c2
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/fakesource.h
@@ -0,0 +1,54 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_FAKESOURCE_H
+#define PHONON_FAKESOURCE_H
+
+#include <QtCore/QSet>
+
+#include "qbasefilter.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class FakePin;
+ class FakeSource : public QBaseFilter
+ {
+ public:
+ FakeSource();
+ ~FakeSource();
+
+ void addAvailableAudioPin(FakePin*);
+ void addAvailableVideoPin(FakePin*);
+ void removeAvailableAudioPin(FakePin*);
+ void removeAvailableVideoPin(FakePin*);
+ private:
+ void createFakeVideoPin();
+ void createFakeAudioPin();
+
+ QSet<FakePin*> availableAudioPins;
+ QSet<FakePin*> availableVideoPins;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/iodevicereader.cpp b/src/3rdparty/phonon/ds9/iodevicereader.cpp
new file mode 100644
index 0000000..ec10278
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/iodevicereader.cpp
@@ -0,0 +1,228 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "iodevicereader.h"
+#include "qasyncreader.h"
+
+#include "mediagraph.h"
+
+#include <phonon/streaminterface.h>
+
+#include <dshow.h>
+#include <initguid.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ //these mediatypes define a stream, its type will be autodetected by DirectShow
+ static QVector<AM_MEDIA_TYPE> getMediaTypes()
+ {
+ AM_MEDIA_TYPE mt;
+ mt.majortype = MEDIATYPE_Stream;
+ mt.bFixedSizeSamples = TRUE;
+ mt.bTemporalCompression = FALSE;
+ mt.lSampleSize = 1;
+ mt.formattype = GUID_NULL;
+ mt.pUnk = 0;
+ mt.cbFormat = 0;
+ mt.pbFormat = 0;
+
+ QVector<AM_MEDIA_TYPE> ret;
+ //normal auto-detect stream
+ mt.subtype = MEDIASUBTYPE_NULL;
+ ret << mt;
+ //AVI stream
+ mt.subtype = MEDIASUBTYPE_Avi;
+ ret << mt;
+ //WAVE stream
+ mt.subtype = MEDIASUBTYPE_WAVE;
+ ret << mt;
+ return ret;
+ }
+
+ class StreamReader : public QAsyncReader, public Phonon::StreamInterface
+ {
+ public:
+ StreamReader(QBaseFilter *parent, const Phonon::MediaSource &source, const MediaGraph *mg) :
+ QAsyncReader(parent, getMediaTypes()),
+ m_seekable(false), m_pos(0), m_size(-1), m_mediaGraph(mg)
+ {
+ connectToSource(source);
+ }
+
+ //for Phonon::StreamInterface
+ void writeData(const QByteArray &data)
+ {
+ QWriteLocker locker(&m_lock);
+ m_pos += data.size();
+ m_buffer += data;
+ }
+
+ void endOfData()
+ {
+ }
+
+ void setStreamSize(qint64 newSize)
+ {
+ QWriteLocker locker(&m_lock);
+ m_size = newSize;
+ }
+
+ qint64 streamSize() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_size;
+ }
+
+ void setStreamSeekable(bool s)
+ {
+ QWriteLocker locker(&m_lock);
+ m_seekable = s;
+ }
+
+ bool streamSeekable() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_seekable;
+ }
+
+ void setCurrentPos(qint64 pos)
+ {
+ QWriteLocker locker(&m_lock);
+ m_pos = pos;
+ seekStream(pos);
+ m_buffer.clear();
+ }
+
+ qint64 currentPos() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_pos;
+ }
+
+ int currentBufferSize() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_buffer.size();
+ }
+
+ //virtual pure members
+
+ //implementation from IAsyncReader
+ STDMETHODIMP Length(LONGLONG *total, LONGLONG *available)
+ {
+ QReadLocker locker(&m_lock);
+ if (total) {
+ *total = m_size;
+ }
+
+ if (available) {
+ *available = m_size;
+ }
+
+ return S_OK;
+ }
+
+
+ HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual)
+ {
+ QMutexLocker locker(&m_mutexRead);
+
+ if (m_mediaGraph->isStopping()) {
+ return VFW_E_WRONG_STATE;
+ }
+
+ if(streamSize() != 1 && pos + length > streamSize()) {
+ //it tries to read outside of the boundaries
+ return E_FAIL;
+ }
+
+ if (currentPos() - currentBufferSize() != pos) {
+ if (!streamSeekable()) {
+ return S_FALSE;
+ }
+ setCurrentPos(pos);
+ }
+
+ int oldSize = currentBufferSize();
+ while (currentBufferSize() < int(length)) {
+ needData();
+ if (m_mediaGraph->isStopping()) {
+ return VFW_E_WRONG_STATE;
+ }
+
+ if (oldSize == currentBufferSize()) {
+ break; //we didn't get any data
+ }
+ oldSize = currentBufferSize();
+ }
+
+ DWORD bytesRead = qMin(currentBufferSize(), int(length));
+ {
+ QWriteLocker locker(&m_lock);
+ qMemCopy(buffer, m_buffer.data(), bytesRead);
+ //truncate the buffer
+ m_buffer = m_buffer.mid(bytesRead);
+ }
+
+ if (actual) {
+ *actual = bytesRead; //initialization
+ }
+
+ return bytesRead == length ? S_OK : S_FALSE;
+ }
+
+ public:
+ //for Phonon::StreamInterface
+ QByteArray m_buffer;
+ bool m_seekable;
+ qint64 m_pos;
+ qint64 m_size;
+
+ QMutex m_mutexRead;
+ const MediaGraph *m_mediaGraph;
+ };
+
+ IODeviceReader::IODeviceReader(const Phonon::MediaSource &source, const MediaGraph *mg) :
+ QBaseFilter(CLSID_NULL)
+ {
+ //create the output pin
+ m_streamReader = new StreamReader(this, source, mg);
+ }
+
+ IODeviceReader::~IODeviceReader()
+ {
+ }
+
+ STDMETHODIMP IODeviceReader::Stop()
+ {
+ HRESULT hr = QBaseFilter::Stop();
+ m_streamReader->enoughData(); //this asks to cancel any blocked call to needData
+ return hr;
+ }
+
+ }
+}
+
+#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/iodevicereader.h b/src/3rdparty/phonon/ds9/iodevicereader.h
new file mode 100644
index 0000000..af4b271
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/iodevicereader.h
@@ -0,0 +1,57 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_IODEVICEREADER_H
+#define PHONON_IODEVICEREADER_H
+
+#include "qbasefilter.h"
+
+#include <phonon/mediasource.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
+
+namespace Phonon
+{
+ class MediaSource;
+ namespace DS9
+ {
+ class MediaGraph;
+ class StreamReader;
+
+ //for the streams...
+ //the filter
+ class IODeviceReader : public QBaseFilter
+ {
+ public:
+ IODeviceReader(const MediaSource &source, const MediaGraph *);
+ ~IODeviceReader();
+ STDMETHODIMP Stop();
+
+ private:
+ StreamReader *m_streamReader;
+ };
+
+ }
+}
+
+#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/lgpl-2.1.txt b/src/3rdparty/phonon/ds9/lgpl-2.1.txt
new file mode 100644
index 0000000..5ab7695
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/lgpl-2.1.txt
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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) any later version.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/src/3rdparty/phonon/ds9/lgpl-3.txt b/src/3rdparty/phonon/ds9/lgpl-3.txt
new file mode 100644
index 0000000..fc8a5de
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/lgpl-3.txt
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/src/3rdparty/phonon/ds9/mediagraph.cpp b/src/3rdparty/phonon/ds9/mediagraph.cpp
new file mode 100644
index 0000000..31a0622
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/mediagraph.cpp
@@ -0,0 +1,1099 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "fakesource.h"
+#include "iodevicereader.h"
+#include "qaudiocdreader.h"
+
+#include "mediagraph.h"
+#include "mediaobject.h"
+
+
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+
+#include <qnetwork.h>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ //description of a connection
+ struct GraphConnection
+ {
+ Filter output;
+ int outputOffset;
+ Filter input;
+ int inputOffset;
+ };
+
+ static QList<GraphConnection> getConnections(Filter source)
+ {
+ QList<GraphConnection> ret;
+ int outOffset = 0;
+ const QList<OutputPin> outputs = BackendNode::pins(source, PINDIR_OUTPUT);
+ for (int i = 0; i < outputs.count(); ++i) {
+ InputPin input;
+ if (outputs.at(i)->ConnectedTo(input.pparam()) == S_OK) {
+ PIN_INFO info;
+ input->QueryPinInfo(&info);
+ Filter current(info.pFilter);
+ if (current) {
+ //this is a valid connection
+ const int inOffset = BackendNode::pins(current, PINDIR_INPUT).indexOf(input);
+ const GraphConnection connection = {source, outOffset, current, inOffset};
+ ret += connection;
+ ret += getConnections(current); //get subsequent connections
+ }
+ }
+ outOffset++;
+ }
+ return ret;
+ }
+
+ static HRESULT saveToFile(Graph graph, const QString &filepath)
+ {
+ const WCHAR wszStreamName[] = L"ActiveMovieGraph";
+ HRESULT hr;
+ ComPointer<IStorage> storage;
+
+ // First, create a document file that will hold the GRF file
+ hr = StgCreateDocfile((OLECHAR*)filepath.utf16(),
+ STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE |
+ STGM_SHARE_EXCLUSIVE,
+ 0, storage.pparam());
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Next, create a stream to store.
+ ComPointer<IStream> stream;
+ hr = storage->CreateStream(wszStreamName,
+ STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
+ 0, 0, stream.pparam());
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // The IpersistStream::Save method converts a stream into a persistent object.
+ ComPointer<IPersistStream> persist(graph, IID_IPersistStream);
+ hr = persist->Save(stream, TRUE);
+ if (SUCCEEDED(hr)) {
+ hr = storage->Commit(STGC_DEFAULT);
+ }
+
+ return hr;
+ }
+
+
+ MediaGraph::MediaGraph(MediaObject *mo, short index) :
+ m_graph(CLSID_FilterGraph, IID_IGraphBuilder),
+ m_fakeSource(new FakeSource()),
+ m_hasVideo(false), m_hasAudio(false), m_connectionsDirty(false),
+ m_isStopping(false), m_isSeekable(false), m_result(S_OK),
+ m_index(index), m_renderId(0), m_seekId(0),
+ m_currentTime(0), m_totalTime(0), m_mediaObject(mo)
+ {
+ m_mediaControl = ComPointer<IMediaControl>(m_graph, IID_IMediaControl);
+ Q_ASSERT(m_mediaControl);
+ m_mediaSeeking = ComPointer<IMediaSeeking>(m_graph, IID_IMediaSeeking);
+ Q_ASSERT(m_mediaSeeking);
+
+ HRESULT hr = m_graph->AddFilter(m_fakeSource, 0);
+ if (m_mediaObject->catchComError(hr)) {
+ return;
+ }
+ }
+
+ MediaGraph::~MediaGraph()
+ {
+ }
+
+ short MediaGraph::index() const
+ {
+ return m_index;
+ }
+
+ void MediaGraph::grabNode(BackendNode *node)
+ {
+ grabFilter(node->filter(m_index));
+ }
+
+ void MediaGraph::grabFilter(Filter filter)
+ {
+ if (filter) {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ if (info.pGraph != m_graph) {
+ if (info.pGraph) {
+ m_mediaObject->catchComError(info.pGraph->RemoveFilter(filter));
+ }
+ m_mediaObject->catchComError(m_graph->AddFilter(filter, 0));
+ }
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+ }
+
+ void MediaGraph::switchFilters(Filter oldFilter, Filter newFilter)
+ {
+ OAFilterState state = syncGetRealState();
+ if (state != State_Stopped) {
+ ensureStopped(); //to do the transaction
+ }
+
+
+ OutputPin connected;
+ {
+ InputPin pin = BackendNode::pins(oldFilter, PINDIR_INPUT).first();
+ pin->ConnectedTo(connected.pparam());
+ }
+
+ m_graph->RemoveFilter(oldFilter);
+ m_graph->AddFilter(newFilter, 0);
+
+ if (connected) {
+ InputPin pin = BackendNode::pins(newFilter, PINDIR_INPUT).first();
+ //let's reestablish the connections
+ m_graph->Connect(connected, pin);
+ }
+
+ switch(state)
+ {
+ case State_Running:
+ play();
+ break;
+ case State_Paused:
+ pause();
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ OAFilterState MediaGraph::syncGetRealState() const
+ {
+ OAFilterState state;
+ m_mediaControl->GetState(INFINITE, &state);
+ return state;
+ }
+
+
+
+ void MediaGraph::ensureSourceDisconnected()
+ {
+ for (int i = 0; i < m_sinkConnections.count(); ++i) {
+ const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
+ const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
+ const QList<InputPin> outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
+
+ for (int i = 0; i < inputs.count(); ++i) {
+ for (int o = 0; o < outputs.count(); o++) {
+ tryDisconnect(outputs.at(o), inputs.at(i));
+ }
+
+ for (int d = 0; d < m_decoderPins.count(); ++d) {
+ tryDisconnect(m_decoderPins.at(d), inputs.at(i));
+ }
+ }
+ }
+ }
+
+ void MediaGraph::ensureSourceConnectedTo(bool force)
+ {
+ if (m_connectionsDirty == false && force == false) {
+ return;
+ }
+
+ m_connectionsDirty = false;
+ ensureSourceDisconnected();
+
+ //reconnect the pins
+ for (int i = 0; i < m_sinkConnections.count(); ++i) {
+ const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
+ const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
+ for(int i = 0; i < inputs.count(); ++i) {
+ //we ensure the filter belongs to the graph
+ grabFilter(currentFilter);
+
+ for (int d = 0; d < m_decoderPins.count(); ++d) {
+ //a decoder has only one output
+ if (tryConnect(m_decoderPins.at(d), inputs.at(i))) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ QList<Filter> MediaGraph::getAllFilters(Graph graph)
+ {
+ QList<Filter> ret;
+ ComPointer<IEnumFilters> enumFilters;
+ graph->EnumFilters(enumFilters.pparam());
+ Filter current;
+ while( enumFilters && enumFilters->Next(1, current.pparam(), 0) == S_OK) {
+ ret += current;
+ }
+ return ret;
+ }
+
+ QList<Filter> MediaGraph::getAllFilters() const
+ {
+ return getAllFilters(m_graph);
+ }
+
+
+ bool MediaGraph::isSeekable() const
+ {
+ return m_isSeekable;
+ }
+
+ qint64 MediaGraph::absoluteTotalTime() const
+ {
+ if (m_seekId) {
+ return m_totalTime;
+ } else {
+ qint64 ret = 0;
+ if (m_mediaSeeking) {
+ m_mediaSeeking->GetDuration(&ret);
+ ret /= 10000; //convert to milliseconds
+ }
+ return ret;
+ }
+ }
+
+ qint64 MediaGraph::absoluteCurrentTime() const
+ {
+ if (m_seekId) {
+ return m_currentTime;
+ } else {
+ qint64 ret = -1;
+ if (m_mediaSeeking) {
+ HRESULT hr = m_mediaSeeking->GetCurrentPosition(&ret);
+ if (FAILED(hr)) {
+ return ret;
+ }
+ ret /= 10000; //convert to milliseconds
+ }
+ return ret;
+ }
+ }
+
+ Phonon::MediaSource MediaGraph::mediaSource() const
+ {
+ return m_mediaSource;
+ }
+
+ void MediaGraph::play()
+ {
+ ensureSourceConnectedTo();
+ m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Running, m_decoders);
+ }
+
+ void MediaGraph::pause()
+ {
+ ensureSourceConnectedTo();
+ m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Paused, m_decoders);
+ }
+
+ HRESULT MediaGraph::renderResult() const
+ {
+ return m_result;
+ }
+
+ bool MediaGraph::isStopping() const
+ {
+ return m_isStopping;
+ }
+
+ Graph MediaGraph::graph() const
+ {
+ return m_graph;
+ }
+
+ void MediaGraph::stop()
+ {
+ if (!isLoading()) {
+ ensureStopped();
+ absoluteSeek(0); //resets the clock
+ } else {
+ m_mediaObject->workerThread()->abortCurrentRender(m_renderId);
+ m_renderId = 0; //cancels current loading
+ }
+ m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Stopped);
+ }
+
+ void MediaGraph::ensureStopped()
+ {
+ m_isStopping = true;
+ //special case here because we want stopped to be synchronous
+ m_graph->Abort();
+ m_mediaControl->Stop();
+ OAFilterState dummy;
+ //this will wait until the change is effective
+ m_mediaControl->GetState(INFINITE, &dummy);
+ m_isStopping = false;
+ }
+
+ bool MediaGraph::isLoading() const
+ {
+ return m_renderId != 0;
+ }
+
+ void MediaGraph::absoluteSeek(qint64 time)
+ {
+ //this just sends a request
+ if (m_seekId == 0) {
+ m_currentTime = absoluteCurrentTime();
+ m_totalTime = absoluteTotalTime();
+ }
+ m_seekId = m_mediaObject->workerThread()->addSeekRequest(m_graph, time);
+ }
+
+ HRESULT MediaGraph::removeFilter(const Filter& filter)
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+#ifdef GRAPH_DEBUG
+ qDebug() << "removeFilter" << QString::fromUtf16(info.achName);
+#endif
+ if (info.pGraph) {
+ info.pGraph->Release();
+ return m_graph->RemoveFilter(filter);
+ }
+
+ //already removed
+ return S_OK;
+ }
+
+ HRESULT MediaGraph::cleanup()
+ {
+ stop();
+
+ ensureSourceDisconnected();
+
+ QList<Filter> list = m_decoders;
+ if (m_demux) {
+ list << m_demux;
+ }
+ if (m_realSource) {
+ list << m_realSource;
+ }
+ list << m_decoders;
+
+ for (int i = 0; i < m_decoders.count(); ++i) {
+ list += getFilterChain(m_demux, m_decoders.at(i));
+ }
+
+ for (int i = 0; i < list.count(); ++i) {
+ removeFilter(list.at(i));
+ }
+
+ //Let's reinitialize the internal lists
+ m_decoderPins.clear();
+ m_decoders.clear();
+ m_demux = Filter();
+ m_realSource = Filter();
+ m_mediaSource = Phonon::MediaSource();
+
+ absoluteSeek(0); //resets the clock
+
+ return S_OK;
+ }
+
+
+ bool MediaGraph::disconnectNodes(BackendNode *source, BackendNode *sink)
+ {
+ const Filter sinkFilter = sink->filter(m_index);
+ const QList<InputPin> inputs = BackendNode::pins(sinkFilter, PINDIR_INPUT);
+
+ QList<OutputPin> outputs;
+ if (source == m_mediaObject) {
+ outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
+ outputs += m_decoderPins;
+ } else {
+ outputs = BackendNode::pins(source->filter(m_index), PINDIR_OUTPUT);
+ }
+
+
+ for (int i = 0; i < inputs.count(); ++i) {
+ for (int o = 0; o < outputs.count(); ++o) {
+ tryDisconnect(outputs.at(o), inputs.at(i));
+ }
+ }
+
+ if (m_sinkConnections.removeOne(sink)) {
+ m_connectionsDirty = true;
+ }
+ return true;
+ }
+
+ bool MediaGraph::tryDisconnect(const OutputPin &out, const InputPin &in)
+ {
+ bool ret = false;
+
+ OutputPin output;
+ if (SUCCEEDED(in->ConnectedTo(output.pparam()))) {
+
+ if (output == out) {
+ //we need a simple disconnection
+ ret = SUCCEEDED(out->Disconnect()) && SUCCEEDED(in->Disconnect());
+ } else {
+ InputPin in2;
+ if (SUCCEEDED(out->ConnectedTo(in2.pparam()))) {
+ PIN_INFO info;
+ in2->QueryPinInfo(&info);
+ Filter tee(info.pFilter);
+ CLSID clsid;
+ tee->GetClassID(&clsid);
+ if (clsid == CLSID_InfTee) {
+ //we have to remove all intermediate filters between the tee and the sink
+ PIN_INFO info;
+ in->QueryPinInfo(&info);
+ Filter sink(info.pFilter);
+ QList<Filter> list = getFilterChain(tee, sink);
+ out->QueryPinInfo(&info);
+ Filter source(info.pFilter);
+
+ if (list.isEmpty()) {
+ output->QueryPinInfo(&info);
+ if (Filter(info.pFilter) == tee) {
+ ret = SUCCEEDED(output->Disconnect()) && SUCCEEDED(in->Disconnect());
+ }
+ } else {
+ ret = true;
+ for (int i = 0; i < list.count(); ++i) {
+ ret = ret && SUCCEEDED(removeFilter(list.at(i)));
+ }
+ }
+
+ //Let's try to see if the Tee filter is still useful
+ if (ret) {
+ int connections = 0;
+ const QList<OutputPin> outputs = BackendNode::pins(tee, PINDIR_OUTPUT);
+ for(int i = 0; i < outputs.count(); ++i) {
+ InputPin p;
+ if ( SUCCEEDED(outputs.at(i)->ConnectedTo(p.pparam()))) {
+ connections++;
+ }
+ }
+ if (connections == 0) {
+ //this avoids a crash if the filter is destroyed
+ //by the subsequent call to removeFilter
+ output = OutputPin();
+ removeFilter(tee); //there is no more output for the tee, we remove it
+ }
+ }
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
+ bool MediaGraph::tryConnect(const OutputPin &out, const InputPin &newIn)
+ {
+
+
+ ///The management of the creation of the Tees is done here (this is the only place where we call IPin::Connect
+ InputPin inPin;
+ if (SUCCEEDED(out->ConnectedTo(inPin.pparam()))) {
+
+ //the fake source has another mechanism for the connection
+ if (BackendNode::pins(m_fakeSource, PINDIR_OUTPUT).contains(out)) {
+ return false;
+ }
+
+ //the output pin is already connected
+ PIN_INFO info;
+ inPin->QueryPinInfo(&info);
+ Filter filter(info.pFilter); //this will ensure the interface is "Release"d
+ CLSID clsid;
+ filter->GetClassID(&clsid);
+ if (clsid == CLSID_InfTee) {
+ //there is already a Tee (namely 'filter') in use
+ const QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
+ for(int i = 0; i < outputs.count(); ++i) {
+ const OutputPin &pin = outputs.at(i);
+ if (VFW_E_NOT_CONNECTED == pin->ConnectedTo(inPin.pparam())) {
+ return SUCCEEDED(pin->Connect(newIn, 0));
+ }
+ }
+ //we shoud never go here
+ return false;
+ } else {
+ QAMMediaType type;
+ out->ConnectionMediaType(&type);
+
+ //first we disconnect the current connection (and we save the current media type)
+ if (!tryDisconnect(out, inPin)) {
+ return false;
+ }
+
+ //..then we try to connect the new node
+ if (SUCCEEDED(out->Connect(newIn, 0))) {
+
+ //we have to insert the Tee
+ if (!tryDisconnect(out, newIn)) {
+ return false;
+ }
+
+ Filter filter(CLSID_InfTee, IID_IBaseFilter);
+ if (!filter) {
+ //rollback
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ if (FAILED(m_graph->AddFilter(filter, 0))) {
+ return false;
+ }
+
+
+ InputPin teeIn = BackendNode::pins(filter, PINDIR_INPUT).first(); //a Tee has always one input
+ HRESULT hr = out->Connect(teeIn, &type);
+ if (FAILED(hr)) {
+ hr = m_graph->Connect(out, teeIn);
+ }
+ if (FAILED(hr)) {
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ OutputPin teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
+
+ //we simply reconnect the pins as they
+ hr = m_graph->Connect(teeOut, inPin);
+ if (FAILED(hr)) {
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
+ if (FAILED(m_graph->Connect(teeOut, newIn))) {
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ return true;
+ } else {
+ //we simply reconnect the pins as they
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+ }
+
+ } else {
+ return SUCCEEDED(m_graph->Connect(out, newIn));
+ }
+ }
+
+ bool MediaGraph::connectNodes(BackendNode *source, BackendNode *sink)
+ {
+ bool ret = false;
+ const QList<InputPin> inputs = BackendNode::pins(sink->filter(m_index), PINDIR_INPUT);
+ QList<OutputPin> outputs = BackendNode::pins(source == m_mediaObject ? m_fakeSource : source->filter(m_index), PINDIR_OUTPUT);
+
+ if (source == m_mediaObject) {
+ grabFilter(m_fakeSource);
+ }
+
+#ifdef GRAPH_DEBUG
+ qDebug() << Q_FUNC_INFO << source << sink << this;
+#endif
+
+ for (int o = 0; o < outputs.count(); o++) {
+ InputPin p;
+ for (int i = 0; i < inputs.count(); i++) {
+ const InputPin &inPin = inputs.at(i);
+ if (tryConnect(outputs.at(o), inPin)) {
+ //tell the sink node that it just got a new input
+ sink->connected(source, inPin);
+ ret = true;
+ if (source == m_mediaObject) {
+ m_connectionsDirty = true;
+ m_sinkConnections += sink;
+#ifdef GRAPH_DEBUG
+ qDebug() << "found a sink connection" << sink << m_sinkConnections.count();
+#endif
+ }
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+
+ HRESULT MediaGraph::loadSource(const Phonon::MediaSource &source)
+ {
+ m_hasVideo = false;
+ m_hasAudio = false;
+ m_isSeekable = false;
+
+
+ //cleanup of the previous filters
+ m_result = cleanup();
+ if (FAILED(m_result)) {
+ return m_result;
+ }
+
+ m_mediaSource = source;
+
+ switch (source.type())
+ {
+ case Phonon::MediaSource::Disc:
+ if (source.discType() == Phonon::Dvd) {
+ m_result = E_NOTIMPL;
+ /*m_realSource = Filter(CLSID_DVDNavigator, IID_IBaseFilter);
+ if (m_realSource) {
+ return REGDB_E_CLASSNOTREG;
+ }
+
+ m_result = m_graph->AddFilter(m_realSource, L"DVD Navigator");*/
+
+
+ #ifndef QT_NO_PHONON_MEDIACONTROLLER
+ } else if (source.discType() == Phonon::Cd) {
+ m_realSource = Filter(new QAudioCDPlayer);
+ m_result = m_graph->AddFilter(m_realSource, 0);
+
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+ } else {
+ m_result = E_NOTIMPL;
+ }
+ if (FAILED(m_result)) {
+ return m_result;
+ }
+ m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
+ return m_result;
+ case Phonon::MediaSource::Invalid:
+ return m_result;
+ case Phonon::MediaSource::Url:
+ case Phonon::MediaSource::LocalFile:
+ {
+ QString url;
+ if (source.type() == Phonon::MediaSource::LocalFile) {
+ url = source.fileName();
+ } else {
+ url = source.url().toString();
+ }
+ m_renderId = m_mediaObject->workerThread()->addUrlToRender(url);
+ }
+ break;
+#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ case Phonon::MediaSource::Stream:
+ {
+ m_realSource = Filter(new IODeviceReader(source, this));
+ m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
+ }
+ break;
+#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ default:
+ m_result = E_FAIL;
+ }
+
+ return m_result;
+ }
+
+ void MediaGraph::finishSeeking(quint16 workId, qint64 time)
+ {
+ if (m_seekId == workId) {
+ m_currentTime = time;
+ m_mediaObject->seekingFinished(this);
+ m_seekId = 0;
+ } else {
+ //it's a queue seek command
+ //we're still seeking
+ }
+ }
+
+ void MediaGraph::finishLoading(quint16 workId, HRESULT hr, Graph graph)
+ {
+ if (m_renderId == workId) {
+ m_renderId = 0;
+
+ //let's determine if the graph is seekable
+ {
+ ComPointer<IMediaSeeking> mediaSeeking(graph, IID_IMediaSeeking);
+ DWORD caps = AM_SEEKING_CanSeekAbsolute;
+ m_isSeekable = mediaSeeking && SUCCEEDED(mediaSeeking->CheckCapabilities(&caps));
+ }
+
+ m_result = reallyFinishLoading(hr, graph);
+ m_mediaObject->loadingFinished(this);
+ }
+ }
+
+
+ HRESULT MediaGraph::reallyFinishLoading(HRESULT hr, const Graph &graph)
+ {
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ const Graph oldGraph = m_graph;
+ m_graph = graph;
+
+ //we keep the source and all the way down to the decoders
+ QList<Filter> removedFilters;
+
+ const QList<Filter> allFilters = getAllFilters(graph);
+ for (int i = 0; i < allFilters.count(); ++i) {
+ const Filter &filter = allFilters.at(i);
+ if (isSourceFilter(filter)) {
+ m_realSource = filter; //save the source filter
+ if (!m_demux ) {
+ m_demux = filter; //in the WMV case, the demuxer is the source filter itself
+ }
+ } else if (isDemuxerFilter(filter)) {
+ m_demux = filter;
+ } else if (isDecoderFilter(filter)) {
+ m_decoders += filter;
+ m_decoderPins += BackendNode::pins(filter, PINDIR_OUTPUT).first();
+ } else {
+ removedFilters += filter;
+ }
+ }
+
+ for (int i = 0; i < m_decoders.count(); ++i) {
+ QList<Filter> chain = getFilterChain(m_demux, m_decoders.at(i));
+ for (int i = 0; i < chain.count(); ++i) {
+ //we keep those filters
+ removedFilters.removeOne(chain.at(i));
+ }
+ }
+
+ for (int i = 0; i < removedFilters.count(); ++i) {
+ graph->RemoveFilter(removedFilters.at(i));
+ }
+
+ m_mediaObject->workerThread()->replaceGraphForEventManagement(graph, oldGraph);
+
+ //let's transfer the nodes from the current graph to the new one
+ QList<GraphConnection> connections; //we store the connections that need to be restored
+
+ // First get all the sink nodes (nodes with no input connected)
+ for (int i = 0; i < m_sinkConnections.count(); ++i) {
+ Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
+ connections += getConnections(currentFilter);
+ grabFilter(currentFilter);
+ }
+
+ //we need to do something smart to detect if the streams are unencoded
+ if (m_demux) {
+ const QList<OutputPin> outputs = BackendNode::pins(m_demux, PINDIR_OUTPUT);
+ for (int i = 0; i < outputs.count(); ++i) {
+ const OutputPin &out = outputs.at(i);
+ InputPin pin;
+ if (out->ConnectedTo(pin.pparam()) == VFW_E_NOT_CONNECTED) {
+ m_decoderPins += out; //unconnected outputs can be decoded outputs
+ }
+ }
+ }
+
+ ensureSourceConnectedTo(true);
+
+ //let's reestablish the connections
+ for (int i = 0; i < connections.count(); ++i) {
+ const GraphConnection &connection = connections.at(i);
+ //check if we shoud transfer the sink node
+
+ grabFilter(connection.input);
+ grabFilter(connection.output);
+
+ const OutputPin output = BackendNode::pins(connection.output, PINDIR_OUTPUT).at(connection.outputOffset);
+ const InputPin input = BackendNode::pins(connection.input, PINDIR_INPUT).at(connection.inputOffset);
+ HRESULT hr = output->Connect(input, 0);
+ Q_UNUSED(hr);
+ Q_ASSERT( SUCCEEDED(hr));
+ }
+
+ //Finally, let's update the interfaces
+ m_mediaControl = ComPointer<IMediaControl>(graph, IID_IMediaControl);
+ m_mediaSeeking = ComPointer<IMediaSeeking>(graph, IID_IMediaSeeking);
+ return hr;
+ }
+
+ //utility functions
+ //retrieves the filters between source and sink
+ QList<Filter> MediaGraph::getFilterChain(const Filter &source, const Filter &sink)
+ {
+ QList<Filter> ret;
+ Filter current = sink;
+ while (current && BackendNode::pins(current, PINDIR_INPUT).count() == 1 && current != source) {
+ if (current != source)
+ ret += current;
+ InputPin pin = BackendNode::pins(current, PINDIR_INPUT).first();
+ current = Filter();
+ OutputPin output;
+ if (pin->ConnectedTo(output.pparam()) == S_OK) {
+ PIN_INFO info;
+ if (SUCCEEDED(output->QueryPinInfo(&info)) && info.pFilter) {
+ current = Filter(info.pFilter); //this will take care of releasing the interface pFilter
+ }
+ }
+ }
+ if (current != source) {
+ //the soruce and sink don't seem to be connected
+ ret.clear();
+ }
+ return ret;
+ }
+
+ bool MediaGraph::isDecoderFilter(const Filter &filter)
+ {
+ if (filter == 0) {
+ return false;
+ }
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+
+
+ QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
+ QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
+
+ //TODO: find a better way to detect if a node is a decoder
+ if (inputs.count() == 0 || outputs.count() ==0) {
+ return false;
+ }
+
+ //the input pin must be encoded data
+ QAMMediaType type;
+ HRESULT hr = inputs.first()->ConnectionMediaType(&type);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+
+ //...and the output must be decoded
+ QAMMediaType type2;
+ hr = outputs.first()->ConnectionMediaType(&type2);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (type2.majortype != MEDIATYPE_Video &&
+ type2.majortype != MEDIATYPE_Audio) {
+ return false;
+ }
+
+ if (type2.majortype == MEDIATYPE_Video) {
+ m_hasVideo = true;
+ } else {
+ m_hasAudio = true;
+ }
+
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << "found a decoder filter" << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+
+ return true;
+ }
+
+ bool MediaGraph::isSourceFilter(const Filter &filter) const
+ {
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+ //a source filter is one that has no input
+ return BackendNode::pins(filter, PINDIR_INPUT).isEmpty();
+ }
+
+ bool MediaGraph::isDemuxerFilter(const Filter &filter) const
+ {
+ QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
+ QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
+
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+
+ if (inputs.count() != 1 || outputs.count() == 0) {
+ return false; //a demuxer has only one input
+ }
+
+ QAMMediaType type;
+ HRESULT hr = inputs.first()->ConnectionMediaType(&type);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (type.majortype != MEDIATYPE_Stream) {
+ return false;
+ }
+
+ for (int i = 0; i < outputs.count(); ++i) {
+ QAMMediaType type;
+ //for now we support only video and audio
+ hr = outputs.at(i)->ConnectionMediaType(&type);
+ if (SUCCEEDED(hr) &&
+ type.majortype != MEDIATYPE_Video && type.majortype != MEDIATYPE_Audio) {
+ return false;
+ }
+ }
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << "found a demuxer filter" << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+ return true;
+ }
+
+ QMultiMap<QString, QString> MediaGraph::metadata() const
+ {
+ QMultiMap<QString, QString> ret;
+ ComPointer<IAMMediaContent> mediaContent(m_demux, IID_IAMMediaContent);
+ if (mediaContent) {
+ //let's get the meta data
+ BSTR str;
+ HRESULT hr = mediaContent->get_AuthorName(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("ARTIST"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_Title(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("TITLE"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_Description(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("DESCRIPTION"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_Copyright(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("COPYRIGHT"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_MoreInfoText(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("MOREINFO"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ }
+ return ret;
+ }
+
+ Filter MediaGraph::realSource() const
+ {
+ return m_realSource;
+ }
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ void MediaGraph::setStopPosition(qint64 time)
+ {
+ qint64 current = 0,
+ stop = 0;
+ m_mediaSeeking->GetPositions(&current, &stop);
+
+ const bool shouldSeek = current == stop;
+
+ if (time == -1) {
+ HRESULT hr = m_mediaSeeking->GetDuration(&time);
+ if (FAILED(hr)) {
+ return;
+ }
+ } else {
+ time *= 10000;
+ }
+
+ if (time == stop) {
+ //the stop position is already at the right place
+ return;
+ }
+
+ if (shouldSeek) {
+ m_mediaSeeking->SetPositions(&current, AM_SEEKING_AbsolutePositioning,
+ &time, AM_SEEKING_AbsolutePositioning);
+ } else {
+ m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning,
+ &time, AM_SEEKING_AbsolutePositioning);
+ }
+ }
+
+ qint64 MediaGraph::stopPosition() const
+ {
+ qint64 ret;
+ m_mediaSeeking->GetStopPosition(&ret);
+ return ret / 10000;
+
+ }
+
+ QList<qint64> MediaGraph::titles() const
+ {
+ //for now we only manage that for the audio cd
+ ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface);
+ if (titleIFace) {
+ return titleIFace->titles();
+ } else {
+ // the default value: only one title that starts at position 0
+ return QList<qint64>() << 0;
+ }
+ }
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+
+
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/mediagraph.h b/src/3rdparty/phonon/ds9/mediagraph.h
new file mode 100644
index 0000000..13e7bcf
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/mediagraph.h
@@ -0,0 +1,148 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_MEDIAGRAPH_H
+#define PHONON_MEDIAGRAPH_H
+
+#include "backendnode.h"
+#include <QtCore/QMultiMap>
+
+#include <phonon/mediasource.h>
+
+//#define GRAPH_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class MediaObject;
+
+ //in the end we should probably have no more inheritance here: everything should be in the interface of the class
+ //could be nice to then remove all the "*this" in the code of this class
+ class MediaGraph : public QObject
+ {
+ public:
+ MediaGraph(MediaObject *mo, short index);
+ ~MediaGraph();
+ bool isSeekable() const;
+ qint64 absoluteTotalTime() const;
+ qint64 absoluteCurrentTime() const;
+ void play();
+ void stop();
+ void pause();
+ void absoluteSeek(qint64);
+
+ QMultiMap<QString, QString> metadata() const;
+
+ static QList<Filter> getAllFilters(Graph graph);
+ QList<Filter> getAllFilters() const;
+
+ HRESULT loadSource(const Phonon::MediaSource &);
+
+ bool hasVideo() const { return m_hasVideo; }
+ void grabNode(BackendNode *node);
+ void grabFilter(Filter filter);
+
+ //connections of the nodes
+ bool connectNodes(BackendNode *source, BackendNode *sink);
+ bool disconnectNodes(BackendNode *source, BackendNode *sink);
+
+ Phonon::MediaSource mediaSource() const;
+
+ //before loading a source, and after its playback this will be called
+ HRESULT cleanup();
+ void ensureStopped();
+
+ short index() const;
+
+ Filter realSource() const;
+
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ QList<qint64> titles() const;
+ void setStopPosition(qint64 time);
+ qint64 stopPosition() const;
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+ void switchFilters(Filter oldFilter, Filter newFilter);
+ OAFilterState syncGetRealState() const;
+
+ bool isLoading() const;
+ bool isStopping() const;
+ HRESULT renderResult() const;
+
+ Graph graph() const;
+
+ void finishLoading(quint16 workId, HRESULT hr, Graph);
+ void finishSeeking(quint16 workId, qint64 time);
+
+ private:
+ bool isSourceFilter(const Filter &filter) const;
+ bool isDemuxerFilter(const Filter &filter) const;
+ bool isDecoderFilter(const Filter &filter);
+ static QList<Filter> getFilterChain(const Filter &source, const Filter &sink);
+
+ HRESULT reallyFinishLoading(HRESULT, const Graph &graph);
+
+
+ //utility functions
+ void ensureSourceConnectedTo(bool force = false);
+ void ensureSourceDisconnected();
+ bool tryConnect(const OutputPin &, const InputPin &);
+ bool tryDisconnect(const OutputPin &, const InputPin &);
+ HRESULT removeFilter(const Filter& filter);
+
+ //after loading, removes the decoders that are not linked to a sink
+ HRESULT removeUselessDecoders();
+
+ //COM objects
+ Graph m_graph;
+ ComPointer<IMediaControl> m_mediaControl;
+ ComPointer<IMediaSeeking> m_mediaSeeking;
+ Filter m_fakeSource, m_realSource;
+ Filter m_demux;
+ QList<OutputPin> m_decoderPins;
+ QList<Filter> m_decoders;
+
+ bool m_hasVideo;
+ bool m_hasAudio;
+ bool m_connectionsDirty;
+ bool m_isStopping;
+ mutable bool m_isSeekable;
+ HRESULT m_result;
+ quint16 m_index;
+ quint16 m_renderId;
+ quint16 m_seekId;
+
+ //while seeking we need to store the current time
+ qint64 m_currentTime;
+ qint64 m_totalTime;
+
+ MediaObject *m_mediaObject;
+ Phonon::MediaSource m_mediaSource;
+ QList<BackendNode*> m_sinkConnections; //connections to the source
+
+ Q_DISABLE_COPY(MediaGraph);
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // PHONON_MEDIAGRAPH_H
diff --git a/src/3rdparty/phonon/ds9/mediaobject.cpp b/src/3rdparty/phonon/ds9/mediaobject.cpp
new file mode 100644
index 0000000..93a19b0
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/mediaobject.cpp
@@ -0,0 +1,1208 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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/QVector>
+#include <QtCore/QTimerEvent>
+#include <QtCore/QTimer>
+#include <QtCore/QTime>
+#include <QtCore/QLibrary>
+
+#ifndef Q_CC_MSVC
+#include <dshow.h>
+#endif //Q_CC_MSVC
+#include <objbase.h>
+#include <initguid.h>
+#include <qnetwork.h>
+#include <evcode.h>
+
+#include "mediaobject.h"
+#include "videowidget.h"
+#include "audiooutput.h"
+
+
+#include <QtCore/QDebug>
+
+#define TIMER_INTERVAL 16 //... ms for the timer that polls the current state (we use the multimedia timer
+#define PRELOAD_TIME 2000 // 2 seconds to load a source
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ typedef BOOL (WINAPI* LPAMGETERRORTEXT)(HRESULT, WCHAR *, DWORD);
+
+ //first the definition of the WorkerThread class
+ WorkerThread::WorkerThread()
+ : QThread(), m_currentRenderId(0), m_finished(false), m_currentWorkId(1)
+ {
+ }
+
+ WorkerThread::~WorkerThread()
+ {
+ }
+
+ WorkerThread::Work WorkerThread::dequeueWork()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_finished) {
+ return Work();
+ }
+ Work ret = m_queue.dequeue();
+
+ //we ensure to have the wait condition in the right state
+ if (m_queue.isEmpty()) {
+ m_waitCondition.reset();
+ } else {
+ m_waitCondition.set();
+ }
+
+ return ret;
+ }
+
+ void WorkerThread::run()
+ {
+ while (m_finished == false) {
+ HANDLE handles[FILTER_COUNT +1];
+ handles[0] = m_waitCondition;
+ int count = 1;
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ if (m_graphHandle[i].graph) {
+ handles[count++] = m_graphHandle[i].handle;
+ }
+ }
+ DWORD result = ::WaitForMultipleObjects(count, handles, FALSE, INFINITE);
+ if (result == WAIT_OBJECT_0) {
+ if (m_finished) {
+ //that's the end if the thread execution
+ return;
+ }
+
+ handleTask();
+ } else {
+ //this is the event management
+ const Graph &graph = m_graphHandle[result - WAIT_OBJECT_0 - 1].graph;
+ long eventCode;
+ LONG_PTR param1, param2;
+
+ ComPointer<IMediaEvent> mediaEvent(graph, IID_IMediaEvent);
+ mediaEvent->GetEvent(&eventCode, &param1, &param2, 0);
+ emit eventReady(graph, eventCode, param1);
+ mediaEvent->FreeEventParams(eventCode, param1, param2);
+ }
+ }
+ }
+
+ //wants to know as soon as the state is set
+ void WorkerThread::addStateChangeRequest(Graph graph, OAFilterState state, QList<Filter> decoders)
+ {
+ QMutexLocker locker(&m_mutex);
+ bool found = false;
+ //we try to see if there is already an attempt to change the state and we remove it
+ for(int i = 0; !found && i < m_queue.size(); ++i) {
+ const Work &w = m_queue.at(i);
+ if (w.graph == graph && w.task == ChangeState) {
+ found = true;
+ m_queue.removeAt(i);
+ }
+ }
+
+ //now let's create the new task
+ Work w;
+ w.task = ChangeState;
+ w.id = m_currentWorkId++;
+ w.graph = graph;
+ w.state = state;
+ w.decoders = decoders;
+ m_queue.enqueue(w);
+ m_waitCondition.set();
+ }
+
+ quint16 WorkerThread::addSeekRequest(Graph graph, qint64 time)
+ {
+ QMutexLocker locker(&m_mutex);
+ bool found = false;
+ //we try to see if there is already an attempt to seek and we remove it
+ for(int i = 0; !found && i < m_queue.size(); ++i) {
+ const Work &w = m_queue.at(i);
+ if (w.graph == graph && w.task == Seek) {
+ found = true;
+ m_queue.removeAt(i);
+ }
+ }
+
+ Work w;
+ w.task = Seek;
+ //we create a new graph
+ w.graph = graph;
+ w.id = m_currentWorkId++;
+ w.time = time;
+ m_queue.enqueue(w);
+ m_waitCondition.set();
+ return w.id;
+ }
+
+ quint16 WorkerThread::addUrlToRender(const QString &url)
+ {
+ QMutexLocker locker(&m_mutex);
+ Work w;
+ w.task = Render;
+ //we create a new graph
+ w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder);
+ w.url = url;
+ w.url.detach();
+ w.id = m_currentWorkId++;
+ m_queue.enqueue(w);
+ m_waitCondition.set();
+ return w.id;
+ }
+
+ quint16 WorkerThread::addFilterToRender(const Filter &filter)
+ {
+ QMutexLocker locker(&m_mutex);
+ Work w;
+ w.task = Render;
+ //we create a new graph
+ w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder);
+ w.filter = filter;
+ w.id = m_currentWorkId++;
+ m_queue.enqueue(w);
+ m_waitCondition.set();
+ return w.id;
+ }
+
+ void WorkerThread::replaceGraphForEventManagement(Graph newGraph, Graph oldGraph)
+ {
+ QMutexLocker locker(&m_mutex);
+ Work w;
+ w.task = ReplaceGraph;
+ w.graph = newGraph;
+ w.oldGraph = oldGraph;
+ m_queue.enqueue(w);
+ m_waitCondition.set();
+ }
+
+ void WorkerThread::handleTask()
+ {
+ const Work w = dequeueWork();
+
+ if (m_finished) {
+ return;
+ }
+
+ HRESULT hr = S_OK;
+
+ m_currentRender = w.graph;
+ m_currentRenderId = w.id;
+ if (w.task == ReplaceGraph) {
+ QMutexLocker locker(&m_mutex);
+ HANDLE h;
+
+ int index = -1;
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ if (m_graphHandle[i].graph == w.oldGraph) {
+ m_graphHandle[i].graph = Graph();
+ index = i;
+ break;
+ } else if (index == -1 && m_graphHandle[i].graph == 0) {
+ //this is the first available slot
+ index = i;
+ }
+ }
+
+ Q_ASSERT(index != -1);
+
+ //add the new graph
+ if (SUCCEEDED(ComPointer<IMediaEvent>(w.graph, IID_IMediaEvent)
+ ->GetEventHandle(reinterpret_cast<OAEVENT*>(&h)))) {
+ m_graphHandle[index].graph = w.graph;
+ m_graphHandle[index].handle = h;
+ }
+ } else if (w.task == Render) {
+ if (w.filter) {
+ //let's render pins
+ w.graph->AddFilter(w.filter, 0);
+ const QList<OutputPin> outputs = BackendNode::pins(w.filter, PINDIR_OUTPUT);
+ for (int i = 0; i < outputs.count(); ++i) {
+ //blocking call
+ hr = w.graph->Render(outputs.at(i));
+ if (FAILED(hr)) {
+ break;
+ }
+ }
+ } else if (!w.url.isEmpty()) {
+ //let's render a url (blocking call)
+ hr = w.graph->RenderFile(reinterpret_cast<const wchar_t *>(w.url.utf16()), 0);
+ }
+ if (hr != E_ABORT) {
+ emit asyncRenderFinished(w.id, hr, w.graph);
+ }
+ } else if (w.task == Seek) {
+ //that's a seekrequest
+ ComPointer<IMediaSeeking> mediaSeeking(w.graph, IID_IMediaSeeking);
+ qint64 newtime = w.time * 10000;
+ hr = mediaSeeking->SetPositions(&newtime, AM_SEEKING_AbsolutePositioning,
+ 0, AM_SEEKING_NoPositioning);
+ qint64 currentTime = -1;
+ if (SUCCEEDED(hr)) {
+ hr = mediaSeeking->GetCurrentPosition(&currentTime);
+ if (SUCCEEDED(hr)) {
+ currentTime /= 10000; //convert to ms
+ }
+ }
+ emit asyncSeekingFinished(w.id, currentTime);
+ hr = E_ABORT; //to avoid emitting asyncRenderFinished
+ } else if (w.task == ChangeState) {
+
+ //remove useless decoders
+ QList<Filter> unused;
+ for (int i = 0; i < w.decoders.count(); ++i) {
+ const Filter &filter = w.decoders.at(i);
+ bool used = false;
+ const QList<OutputPin> pins = BackendNode::pins(filter, PINDIR_OUTPUT);
+ for( int i = 0; i < pins.count(); ++i) {
+ InputPin input;
+ if (pins.at(i)->ConnectedTo(input.pparam()) == S_OK) {
+ used = true;
+ }
+ }
+ if (!used) {
+ unused += filter;
+ }
+ }
+
+ //we can get the state
+ for (int i = 0; i < unused.count(); ++i) {
+ //we should remove this filter from the graph
+ w.graph->RemoveFilter(unused.at(i));
+ }
+
+
+ //we can get the state
+ ComPointer<IMediaControl> mc(w.graph, IID_IMediaControl);
+
+ //we change the state here
+ switch(w.state)
+ {
+ case State_Stopped:
+ mc->Stop();
+ break;
+ case State_Paused:
+ mc->Pause();
+ break;
+ case State_Running:
+ mc->Run();
+ break;
+ }
+ OAFilterState s;
+ //blocking call
+ HRESULT hr = mc->GetState(INFINITE, &s);
+
+ if (SUCCEEDED(hr)) {
+ if (s == State_Stopped) {
+ emit stateReady(w.graph, Phonon::StoppedState);
+ } else if (s == State_Paused) {
+ emit stateReady(w.graph, Phonon::PausedState);
+ } else /*if (s == State_Running)*/ {
+ emit stateReady(w.graph, Phonon::PlayingState);
+ }
+ }
+ }
+
+ m_currentRender = Graph();
+ m_currentRenderId = 0;
+
+ }
+
+ void WorkerThread::abortCurrentRender(qint16 renderId)
+ {
+ QMutexLocker locker(&m_mutex);
+ bool found = false;
+ //we try to see if there is already an attempt to seek and we remove it
+ for(int i = 0; !found && i < m_queue.size(); ++i) {
+ const Work &w = m_queue.at(i);
+ if (w.id == renderId) {
+ found = true;
+ m_queue.removeAt(i);
+ }
+ }
+
+ if (m_currentRender && m_currentRenderId == renderId) {
+ m_currentRender->Abort();
+ }
+ }
+
+ //tells the thread to stop processing
+ void WorkerThread::signalStop()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_queue.clear();
+ if (m_currentRender) {
+ //in case we're currently rendering something
+ m_currentRender->Abort();
+
+ }
+
+ m_finished = true;
+ m_waitCondition.set();
+ }
+
+
+ MediaObject::MediaObject(QObject *parent) : BackendNode(parent),
+ transactionState(Phonon::StoppedState),
+ m_errorType(Phonon::NoError),
+ m_state(Phonon::LoadingState),
+ m_nextState(Phonon::StoppedState),
+ m_prefinishMark(0),
+ m_tickInterval(0),
+ m_buffering(false),
+ m_oldHasVideo(false),
+ m_prefinishMarkSent(false),
+ m_aboutToFinishSent(false),
+ m_nextSourceReadyToStart(false),
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ m_autoplayTitles(true),
+ m_currentTitle(0),
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+ m_targetTick(INFINITE)
+ {
+
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ m_graphs[i] = new MediaGraph(this, i);
+ }
+
+ connect(&m_thread, SIGNAL(stateReady(Graph, Phonon::State)),
+ SLOT(slotStateReady(Graph, Phonon::State)));
+
+ connect(&m_thread, SIGNAL(eventReady(Graph, long, long)),
+ SLOT(handleEvents(Graph, long, long)));
+
+ connect(&m_thread, SIGNAL(asyncRenderFinished(quint16, HRESULT, Graph)),
+ SLOT(finishLoading(quint16, HRESULT, Graph)));
+
+ connect(&m_thread, SIGNAL(asyncSeekingFinished(quint16, qint64)),
+ SLOT(finishSeeking(quint16, qint64)));
+ //really special case
+ m_mediaObject = this;
+ m_thread.start();
+ }
+
+ MediaObject::~MediaObject()
+ {
+ //be sure to finish the timer first
+ m_tickTimer.stop();
+
+ //we finish the worker thread here
+ m_thread.signalStop();
+ m_thread.wait();
+
+ //and then we delete the graphs
+ for (int i = 0; i < FILTER_COUNT; ++i) {
+ delete m_graphs[i];
+ }
+ }
+
+ WorkerThread *MediaObject::workerThread()
+ {
+ return &m_thread;
+ }
+
+ MediaGraph *MediaObject::currentGraph() const
+ {
+ return m_graphs[0];
+ }
+
+ MediaGraph *MediaObject::nextGraph() const
+ {
+ return m_graphs[FILTER_COUNT - 1];
+ }
+
+ //utility function to save the graph to a file
+ void MediaObject::timerEvent(QTimerEvent *e)
+ {
+ if (e->timerId() == m_tickTimer.timerId()) {
+
+ const qint64 current = currentTime();
+ const qint64 total = totalTime();
+
+ if ( m_tickInterval != 0 && current > m_targetTick) {
+ updateTargetTick();
+ emit tick(current);
+ }
+
+ //check that the title hasn't changed
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ if (m_autoplayTitles && m_currentTitle < _iface_availableTitles() - 1) {
+
+ if (current >= total) {
+ //we go to the next title
+ _iface_setCurrentTitle(m_currentTitle + 1, false);
+ emit tick(current);
+ }
+ return;
+ }
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+ if (total) {
+ const qint64 remaining = total - current;
+
+ if (m_transitionTime < 0 && m_nextSourceReadyToStart) {
+ if (remaining < -m_transitionTime + TIMER_INTERVAL/2) {
+ //we need to switch graphs to run the next source in the queue (with cross-fading)
+ switchToNextSource();
+ return;
+ } else if (current < -m_transitionTime) {
+ //we are currently crossfading
+ for (int i = 0; i < m_audioOutputs.count(); ++i) {
+ m_audioOutputs.at(i)->setCrossFadingProgress( currentGraph()->index(), qMin( qreal(1.), qreal(current) / qreal(-m_transitionTime)));
+ }
+ }
+ }
+
+ if (m_prefinishMark > 0 && !m_prefinishMarkSent && remaining < m_prefinishMark + TIMER_INTERVAL/2) {
+#ifdef GRAPH_DEBUG
+ qDebug() << "DS9: emit prefinishMarkReached" << remaining << QTime::currentTime().toString();
+#endif
+ m_prefinishMarkSent = true;
+
+ emit prefinishMarkReached( remaining );
+ }
+
+ if (!m_aboutToFinishSent && remaining < PRELOAD_TIME - m_transitionTime + TIMER_INTERVAL/2) {
+ //let's take a 2 seconds time time to actually load the next file
+#ifdef GRAPH_DEBUG
+ qDebug() << "DS9: emit aboutToFinish" << remaining << QTime::currentTime().toString();
+#endif
+ m_aboutToFinishSent = true;
+ emit aboutToFinish();
+ }
+ } else {
+ //total is 0: the stream is probably live (endless)
+ }
+
+ if (m_buffering) {
+ ComPointer<IAMNetworkStatus> status(currentGraph()->realSource(), IID_IAMNetworkStatus);
+ if (status) {
+ long l;
+ status->get_BufferingProgress(&l);
+ emit bufferStatus(l);
+#ifdef GRAPH_DEBUG
+ qDebug() << "emit bufferStatus(" << l << ")";
+#endif
+ }
+ }
+ }
+ }
+
+ void MediaObject::switchToNextSource()
+ {
+ m_prefinishMarkSent = false;
+ m_aboutToFinishSent = false;
+ m_nextSourceReadyToStart = false;
+
+ m_oldHasVideo = currentGraph()->hasVideo();
+
+ qSwap(m_graphs[0], m_graphs[1]); //swap the graphs
+
+ //we tell the video widgets to switch now to the new source
+#ifndef QT_NO_PHONON_VIDEO
+ for (int i = 0; i < m_videoWidgets.count(); ++i) {
+ m_videoWidgets.at(i)->setCurrentGraph(currentGraph()->index());
+ }
+#endif //QT_NO_PHONON_VIDEO
+
+ emit currentSourceChanged(currentGraph()->mediaSource());
+
+ if (currentGraph()->isLoading()) {
+ //will simply tell that when loading is finished
+ //it should start the playback
+ play();
+ }
+
+
+
+ emit metaDataChanged(currentGraph()->metadata());
+
+ if (nextGraph()->hasVideo() != currentGraph()->hasVideo()) {
+ emit hasVideoChanged(currentGraph()->hasVideo());
+ }
+
+ emit tick(0);
+ emit totalTimeChanged(totalTime());
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ setTitles(currentGraph()->titles());
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+ //this manages only gapless transitions
+ if (currentGraph()->mediaSource().type() != Phonon::MediaSource::Invalid) {
+ if (catchComError(currentGraph()->renderResult())) {
+ setState(Phonon::ErrorState);
+ } else {
+ play();
+ }
+ }
+ }
+
+ Phonon::State MediaObject::state() const
+ {
+ if (m_buffering) {
+ return Phonon::BufferingState;
+ } else {
+ return m_state;
+ }
+ }
+
+ bool MediaObject::hasVideo() const
+ {
+ return currentGraph()->hasVideo();
+ }
+
+ bool MediaObject::isSeekable() const
+ {
+ return currentGraph()->isSeekable();
+ }
+
+ qint64 MediaObject::totalTime() const
+ {
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ //1st, check if there is more titles after
+ const qint64 ret = (m_currentTitle < _iface_availableTitles() - 1) ?
+ titleAbsolutePosition(m_currentTitle+1) : currentGraph()->absoluteTotalTime();
+
+ //this is the duration of the current title
+ return ret - titleAbsolutePosition(m_currentTitle);
+#else
+ return currentGraph()->absoluteTotalTime();
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+ }
+
+ qint64 MediaObject::currentTime() const
+ {
+ //this handles inaccuracy when stopping on a title
+ return currentGraph()->absoluteCurrentTime()
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ - titleAbsolutePosition(m_currentTitle)
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+ ;
+ }
+
+ qint32 MediaObject::tickInterval() const
+ {
+ return m_tickInterval;
+ }
+
+ void MediaObject::setTickInterval(qint32 newTickInterval)
+ {
+ m_tickInterval = newTickInterval;
+ updateTargetTick();
+ }
+
+ void MediaObject::pause()
+ {
+ if (currentGraph()->isLoading()) {
+ m_nextState = Phonon::PausedState;
+ } else {
+ currentGraph()->pause();
+ }
+ }
+
+ void MediaObject::stop()
+ {
+ if (currentGraph()->isLoading()) {
+ m_nextState = Phonon::StoppedState;
+ } else {
+ currentGraph()->stop();
+ }
+ }
+
+ void MediaObject::ensureStopped()
+ {
+ currentGraph()->ensureStopped();
+ if (m_state == Phonon::ErrorState) {
+ //we reset the state here
+ m_state = Phonon::StoppedState;
+ }
+ }
+
+ void MediaObject::play()
+ {
+ if (currentGraph()->isLoading()) {
+ m_nextState = Phonon::PlayingState;
+ } else {
+ currentGraph()->play();
+ }
+ }
+
+ QString MediaObject::errorString() const
+ {
+ return m_errorString;
+ }
+
+ Phonon::ErrorType MediaObject::errorType() const
+ {
+ return m_errorType;
+ }
+
+
+ void MediaObject::updateTargetTick()
+ {
+ if (m_tickInterval) {
+ const qint64 current = currentTime();
+ m_targetTick = current / m_tickInterval * m_tickInterval;
+ if (current == 0 || m_targetTick < current) {
+ m_targetTick += m_tickInterval;
+ }
+ }
+ }
+
+ void MediaObject::setState(Phonon::State newstate)
+ {
+ if (newstate == Phonon::PlayingState) {
+ updateTargetTick();
+ }
+
+ if (newstate == m_state) {
+ return;
+ }
+
+ //manage the timer
+ if (newstate == Phonon::PlayingState) {
+ m_tickTimer.start(TIMER_INTERVAL, this);
+ } else {
+ m_tickTimer.stop();
+ }
+
+ Phonon::State oldstate = state();
+ m_state = newstate;
+ emit stateChanged(newstate, oldstate);
+ }
+
+
+ qint32 MediaObject::prefinishMark() const
+ {
+ return m_prefinishMark;
+ }
+
+ void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
+ {
+ m_prefinishMark = newPrefinishMark;
+ }
+
+ qint32 MediaObject::transitionTime() const
+ {
+ return m_transitionTime;
+ }
+
+ void MediaObject::setTransitionTime(qint32 time)
+ {
+ m_transitionTime = time;
+ }
+
+ qint64 MediaObject::remainingTime() const
+ {
+ return totalTime() - currentTime();
+ }
+
+
+ Phonon::MediaSource MediaObject::source() const
+ {
+ return currentGraph()->mediaSource();
+ }
+
+ void MediaObject::setNextSource(const Phonon::MediaSource &source)
+ {
+ m_nextSourceReadyToStart = true;
+ const bool shouldSwitch = (m_state == Phonon::StoppedState || m_state == Phonon::ErrorState);
+ nextGraph()->loadSource(source); //let's preload the source
+
+ if (shouldSwitch) {
+ switchToNextSource();
+ }
+ }
+
+ void MediaObject::setSource(const Phonon::MediaSource &source)
+ {
+ m_nextSourceReadyToStart = false;
+ m_prefinishMarkSent = false;
+ m_aboutToFinishSent = false;
+
+ m_oldHasVideo = currentGraph()->hasVideo();
+ setState(Phonon::LoadingState);
+ //After loading we go into stopped state
+ m_nextState = Phonon::StoppedState;
+ catchComError(currentGraph()->loadSource(source));
+ emit currentSourceChanged(source);
+ }
+
+ void MediaObject::slotStateReady(Graph graph, Phonon::State newState)
+ {
+ if (graph == currentGraph()->graph() && !currentGraph()->isLoading()) {
+ setState(newState);
+ }
+ }
+
+ void MediaObject::loadingFinished(MediaGraph *mg)
+ {
+ if (mg == currentGraph()) {
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ //Title interface
+ m_currentTitle = 0;
+ setTitles(currentGraph()->titles());
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+ HRESULT hr = mg->renderResult();
+
+ if (catchComError(hr)) {
+ return;
+ }
+
+ if (m_oldHasVideo != currentGraph()->hasVideo()) {
+ emit hasVideoChanged(currentGraph()->hasVideo());
+ }
+
+#ifndef QT_NO_PHONON_VIDEO
+ if (currentGraph()->hasVideo()) {
+ updateVideoGeometry();
+ }
+#endif //QT_NO_PHONON_VIDEO
+
+ emit metaDataChanged(currentGraph()->metadata());
+ emit totalTimeChanged(totalTime());
+
+ //let's put the next state
+ switch(m_nextState)
+ {
+ case Phonon::PausedState:
+ pause();
+ break;
+ case Phonon::StoppedState:
+ stop();
+ break;
+ case Phonon::PlayingState:
+ play();
+ break;
+ case Phonon::ErrorState:
+ setState(Phonon::ErrorState);
+ break;
+ }
+ }
+ }
+
+ void MediaObject::seek(qint64 time)
+ {
+ //we seek into the current title
+ currentGraph()->absoluteSeek(time
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ + titleAbsolutePosition(m_currentTitle)
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+ );
+ }
+
+ void MediaObject::seekingFinished(MediaGraph *mg)
+ {
+ if (mg == currentGraph()) {
+
+ updateTargetTick();
+ if (currentTime() < totalTime() - m_prefinishMark) {
+ m_prefinishMarkSent = false;
+ }
+
+ if (currentTime() < totalTime() - PRELOAD_TIME + m_transitionTime) {
+ m_aboutToFinishSent = false;
+ }
+
+ //this helps the update of the application (seekslider for example)
+ if (m_state == PausedState || m_state == PlayingState) {
+ emit tick(currentTime());
+ }
+ }
+ }
+
+
+ bool MediaObject::catchComError(HRESULT hr)
+ {
+
+ m_errorString.clear();
+ m_errorType = Phonon::NoError;
+
+ if (hr != S_OK) {
+#ifdef GRAPH_DEBUG
+ qWarning("an error occurred 0x%x",hr);
+#endif
+ LPAMGETERRORTEXT getErrorText = (LPAMGETERRORTEXT)QLibrary::resolve(QLatin1String("quartz"), "AMGetErrorTextW");
+
+ ushort buffer[MAX_ERROR_TEXT_LEN];
+ if (getErrorText && getErrorText(hr, (WCHAR*)buffer, MAX_ERROR_TEXT_LEN)) {
+ m_errorString = QString::fromUtf16(buffer);
+ } else {
+ m_errorString = QString::fromLatin1("Unknown error");
+ }
+ const QString comError = QString::number(uint(hr), 16);
+ if (!m_errorString.toLower().contains(comError.toLower())) {
+ m_errorString += QString::fromLatin1(" (0x%1)").arg(comError);
+ }
+ if (FAILED(hr)) {
+ m_errorType = Phonon::FatalError;
+ setState(Phonon::ErrorState);
+ } else {
+ m_errorType = Phonon::NormalError;
+ m_nextState = Phonon::ErrorState;
+ }
+ } else {
+ m_errorType = Phonon::NoError;
+
+ }
+
+ return m_errorType == Phonon::FatalError;
+ }
+
+
+ void MediaObject::grabNode(BackendNode *node)
+ {
+ for (int i = 0; i < FILTER_COUNT; ++i) {
+ m_graphs[i]->grabNode(node);
+ }
+ node->setMediaObject(this);
+ }
+
+ bool MediaObject::connectNodes(BackendNode *source, BackendNode *sink)
+ {
+ bool ret = true;
+ for (int i = 0; i < FILTER_COUNT; ++i) {
+ ret = ret && m_graphs[i]->connectNodes(source, sink);
+ }
+ if (ret) {
+#ifndef QT_NO_PHONON_VIDEO
+ if (VideoWidget *video = qobject_cast<VideoWidget*>(sink)) {
+ m_videoWidgets += video;
+ } else
+#endif //QT_NO_PHONON_VIDEO
+ if (AudioOutput *audio = qobject_cast<AudioOutput*>(sink)) {
+ m_audioOutputs += audio;
+ }
+ }
+ return ret;
+ }
+
+ bool MediaObject::disconnectNodes(BackendNode *source, BackendNode *sink)
+ {
+ bool ret = true;
+ for (int i = 0; i < FILTER_COUNT; ++i) {
+ ret = ret && m_graphs[i]->disconnectNodes(source, sink);
+ }
+ if (ret) {
+#ifndef QT_NO_PHONON_VIDEO
+ if (VideoWidget *video = qobject_cast<VideoWidget*>(sink)) {
+ m_videoWidgets.removeOne(video);
+ } else
+#endif //QT_NO_PHONON_VIDEO
+ if (AudioOutput *audio = qobject_cast<AudioOutput*>(sink)) {
+ m_audioOutputs.removeOne(audio);
+ }
+ }
+ return ret;
+ }
+
+#ifndef QT_NO_PHONON_VIDEO
+ void MediaObject::updateVideoGeometry()
+ {
+ for (int i = 0; i < m_videoWidgets.count(); ++i) {
+ m_videoWidgets.at(i)->notifyVideoLoaded();
+ }
+ }
+#endif //QT_NO_PHONON_VIDEO
+
+ void MediaObject::handleComplete(IGraphBuilder *graph)
+ {
+ if (graph == currentGraph()->graph()) {
+ if (m_transitionTime >= PRELOAD_TIME || m_aboutToFinishSent == false) {
+ emit aboutToFinish(); //give a chance to the frontend to give a next source
+ m_aboutToFinishSent = true;
+ }
+
+ if (!m_nextSourceReadyToStart) {
+ //this is the last source, we simply finish
+ const qint64 current = currentTime();
+ const OAFilterState currentState = currentGraph()->syncGetRealState();
+
+ emit tick(current); //this ensures that the end of the seek slider is reached
+ emit finished();
+
+ if (currentTime() == current && currentGraph()->syncGetRealState() == currentState) {
+ //no seek operation in-between
+ pause();
+ setState(Phonon::PausedState); //we set it here
+ }
+
+ } else if (m_transitionTime == 0) {
+ //gapless transition
+ switchToNextSource(); //let's call the function immediately
+ } else if (m_transitionTime > 0) {
+ //management of the transition (if it is >= 0)
+ QTimer::singleShot(m_transitionTime, this, SLOT(switchToNextSource()));
+ }
+ } else {
+ //it is just the end of the previous source (in case of cross-fading)
+ nextGraph()->cleanup();
+ }
+ for (int i = 0; i < m_audioOutputs.count(); ++i) {
+ m_audioOutputs.at(i)->setCrossFadingProgress( currentGraph()->index(), 1.); //cross-fading is in any case finished
+ }
+ }
+
+ void MediaObject::finishLoading(quint16 workId, HRESULT hr, Graph graph)
+ {
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ m_graphs[i]->finishLoading(workId, hr, graph);
+ }
+ }
+
+ void MediaObject::finishSeeking(quint16 workId, qint64 time)
+ {
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ m_graphs[i]->finishSeeking(workId, time);
+ }
+ }
+
+
+ void MediaObject::handleEvents(Graph graph, long eventCode, long param1)
+ {
+ QString eventDescription;
+ switch (eventCode)
+ {
+ case EC_BUFFERING_DATA:
+ if (graph == currentGraph()->graph()) {
+ m_buffering = param1;
+ emit stateChanged(state(), m_state);
+ }
+ break;
+ case EC_LENGTH_CHANGED:
+ if (graph == currentGraph()->graph()) {
+ emit totalTimeChanged( totalTime() );
+ }
+ break;
+
+ case EC_COMPLETE:
+ handleComplete(graph);
+ break;
+
+#ifndef QT_NO_PHONON_VIDEO
+ case EC_VIDEO_SIZE_CHANGED:
+ if (graph == currentGraph()->graph()) {
+ updateVideoGeometry();
+ }
+ break;
+#endif //QT_NO_PHONON_VIDEO
+
+#ifdef GRAPH_DEBUG
+ case EC_ACTIVATE: qDebug() << "EC_ACTIVATE: A video window is being " << (param1 ? "ACTIVATED" : "DEACTIVATED"); break;
+ case EC_BUILT: qDebug() << "EC_BUILT: Send by the Video Control when a graph has been built. Not forwarded to applications."; break;
+ case EC_CLOCK_CHANGED: qDebug() << "EC_CLOCK_CHANGED"; break;
+ case EC_CLOCK_UNSET: qDebug() << "EC_CLOCK_UNSET: The clock provider was disconnected."; break;
+ case EC_CODECAPI_EVENT: qDebug() << "EC_CODECAPI_EVENT: Sent by an encoder to signal an encoding event."; break;
+ case EC_DEVICE_LOST: qDebug() << "EC_DEVICE_LOST: A Plug and Play device was removed or has become available again."; break;
+ case EC_DISPLAY_CHANGED: qDebug() << "EC_DISPLAY_CHANGED: The display mode has changed."; break;
+ case EC_END_OF_SEGMENT: qDebug() << "EC_END_OF_SEGMENT: The end of a segment has been reached."; break;
+ case EC_ERROR_STILLPLAYING: qDebug() << "EC_ERROR_STILLPLAYING: An asynchronous command to run the graph has failed."; break;
+ case EC_ERRORABORT: qDebug() << "EC_ERRORABORT: An operation was aborted because of an error."; break;
+ case EC_EXTDEVICE_MODE_CHANGE: qDebug() << "EC_EXTDEVICE_MODE_CHANGE: Not supported."; break;
+ case EC_FULLSCREEN_LOST: qDebug() << "EC_FULLSCREEN_LOST: The video renderer is switching out of full-screen mode."; break;
+ case EC_GRAPH_CHANGED: qDebug() << "EC_GRAPH_CHANGED: The filter graph has changed."; break;
+ case EC_NEED_RESTART: qDebug() << "EC_NEED_RESTART: A filter is requesting that the graph be restarted."; break;
+ case EC_NOTIFY_WINDOW: qDebug() << "EC_NOTIFY_WINDOW: Notifies a filter of the video renderer's window."; break;
+ case EC_OLE_EVENT: qDebug() << "EC_OLE_EVENT: A filter is passing a text string to the application."; break;
+ case EC_OPENING_FILE: qDebug() << "EC_OPENING_FILE: The graph is opening a file, or has finished opening a file."; break;
+ case EC_PALETTE_CHANGED: qDebug() << "EC_PALETTE_CHANGED: The video palette has changed."; break;
+ case EC_PAUSED: qDebug() << "EC_PAUSED: A pause request has completed."; break;
+ case EC_PREPROCESS_COMPLETE: qDebug() << "EC_PREPROCESS_COMPLETE: Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding."; break;
+ case EC_QUALITY_CHANGE: qDebug() << "EC_QUALITY_CHANGE: The graph is dropping samples, for quality control."; break;
+ case EC_REPAINT: qDebug() << "EC_REPAINT: A video renderer requires a repaint."; break;
+ case EC_SEGMENT_STARTED: qDebug() << "EC_SEGMENT_STARTED: A new segment has started."; break;
+ case EC_SHUTTING_DOWN: qDebug() << "EC_SHUTTING_DOWN: The filter graph is shutting down, prior to being destroyed."; break;
+ case EC_SNDDEV_IN_ERROR: qDebug() << "EC_SNDDEV_IN_ERROR: A device error has occurred in an audio capture filter."; break;
+ case EC_SNDDEV_OUT_ERROR: qDebug() << "EC_SNDDEV_OUT_ERROR: A device error has occurred in an audio renderer filter."; break;
+ case EC_STARVATION: qDebug() << "EC_STARVATION: A filter is not receiving enough data."; break;
+ case EC_STATE_CHANGE: qDebug() << "EC_STATE_CHANGE: The filter graph has changed state."; break;
+ case EC_STEP_COMPLETE: qDebug() << "EC_STEP_COMPLETE: A filter performing frame stepping has stepped the specified number of frames."; break;
+ case EC_STREAM_CONTROL_STARTED: qDebug() << "EC_STREAM_CONTROL_STARTED: A stream-control start command has taken effect."; break;
+ case EC_STREAM_CONTROL_STOPPED: qDebug() << "EC_STREAM_CONTROL_STOPPED: A stream-control stop command has taken effect."; break;
+ case EC_STREAM_ERROR_STILLPLAYING: qDebug() << "EC_STREAM_ERROR_STILLPLAYING: An error has occurred in a stream. The stream is still playing."; break;
+ case EC_STREAM_ERROR_STOPPED: qDebug() << "EC_STREAM_ERROR_STOPPED: A stream has stopped because of an error."; break;
+ case EC_TIMECODE_AVAILABLE: qDebug() << "EC_TIMECODE_AVAILABLE: Not supported."; break;
+ case EC_UNBUILT: qDebug() << "Sent by the Video Control when a graph has been torn down. Not forwarded to applications."; break;
+ case EC_USERABORT: qDebug() << "EC_USERABORT: Send by the Video Control when a graph has been torn down. Not forwarded to applications."; break;
+ case EC_VMR_RECONNECTION_FAILED: qDebug() << "EC_VMR_RECONNECTION_FAILED: Sent by the VMR-7 and the VMR-9 when it was unable to accept a dynamic format change request from the upstream decoder."; break;
+ case EC_VMR_RENDERDEVICE_SET: qDebug() << "EC_VMR_RENDERDEVICE_SET: Sent when the VMR has selected its rendering mechanism."; break;
+ case EC_VMR_SURFACE_FLIPPED: qDebug() << "EC_VMR_SURFACE_FLIPPED: Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented."; break;
+ case EC_WINDOW_DESTROYED: qDebug() << "EC_WINDOW_DESTROYED: The video renderer was destroyed or removed from the graph"; break;
+ case EC_WMT_EVENT: qDebug() << "EC_WMT_EVENT: Sent by the Windows Media Format SDK when an application uses the ASF Reader filter to play ASF files protected by digital rights management (DRM)."; break;
+ case EC_WMT_INDEX_EVENT: qDebug() << "EC_WMT_INDEX_EVENT: Sent by the Windows Media Format SDK when an application uses the ASF Writer to index Windows Media Video files."; break;
+
+ //documented by Microsoft but not supported in the Platform SDK
+ // case EC_BANDWIDTHCHANGE : qDebug() << "EC_BANDWIDTHCHANGE: not supported"; break;
+ // case EC_CONTENTPROPERTY_CHANGED: qDebug() << "EC_CONTENTPROPERTY_CHANGED: not supported."; break;
+ // case EC_EOS_SOON: qDebug() << "EC_EOS_SOON: not supported"; break;
+ // case EC_ERRORABORTEX: qDebug() << "EC_ERRORABORTEX: An operation was aborted because of an error."; break;
+ // case EC_FILE_CLOSED: qDebug() << "EC_FILE_CLOSED: The source file was closed because of an unexpected event."; break;
+ // case EC_LOADSTATUS: qDebug() << "EC_LOADSTATUS: Notifies the application of progress when opening a network file."; break;
+ // case EC_MARKER_HIT: qDebug() << "EC_MARKER_HIT: not supported."; break;
+ // case EC_NEW_PIN: qDebug() << "EC_NEW_PIN: not supported."; break;
+ // case EC_PLEASE_REOPEN: qDebug() << "EC_PLEASE_REOPEN: The source file has changed."; break;
+ // case EC_PROCESSING_LATENCY: qDebug() << "EC_PROCESSING_LATENCY: Indicates the amount of time that a component is taking to process each sample."; break;
+ // case EC_RENDER_FINISHED: qDebug() << "EC_RENDER_FINISHED: Not supported."; break;
+ // case EC_SAMPLE_LATENCY: qDebug() << "EC_SAMPLE_LATENCY: Specifies how far behind schedule a component is for processing samples."; break;
+ // case EC_SAMPLE_NEEDED: qDebug() << "EC_SAMPLE_NEEDED: Requests a new input sample from the Enhanced Video Renderer (EVR) filter."; break;
+ // case EC_SCRUB_TIME: qDebug() << "EC_SCRUB_TIME: Specifies the time stamp for the most recent frame step."; break;
+ // case EC_STATUS: qDebug() << "EC_STATUS: Contains two arbitrary status strings."; break;
+ // case EC_VIDEOFRAMEREADY: qDebug() << "EC_VIDEOFRAMEREADY: A video frame is ready for display."; break;
+
+ default:
+ qDebug() << "Unknown event" << eventCode << "(" << param1 << ")";
+ break;
+#else
+ default:
+ break;
+#endif
+ }
+ }
+
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ //interface management
+ bool MediaObject::hasInterface(Interface iface) const
+ {
+ return iface == AddonInterface::TitleInterface;
+ }
+
+ QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> &params)
+ {
+ if (hasInterface(iface)) {
+
+ switch (iface)
+ {
+ case TitleInterface:
+ switch (command)
+ {
+ case availableTitles:
+ return _iface_availableTitles();
+ case title:
+ return _iface_currentTitle();
+ case setTitle:
+ _iface_setCurrentTitle(params.first().toInt());
+ break;
+ case autoplayTitles:
+ return m_autoplayTitles;
+ case setAutoplayTitles:
+ m_autoplayTitles = params.first().toBool();
+ updateStopPosition();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return QVariant();
+ }
+
+
+ //TitleInterface
+
+ //this is called to set the time for the different titles
+ qint64 MediaObject::titleAbsolutePosition(int title) const
+ {
+ if (title >= 0 && title < m_titles.count()) {
+ return m_titles.at(title);
+ } else {
+ return 0;
+ }
+ }
+
+ void MediaObject::setTitles(const QList<qint64> &titles)
+ {
+ //this is called when the source is loaded
+ const bool emitSignal = m_titles.count() != titles.count();
+ m_titles = titles;
+ if (emitSignal) {
+ emit availableTitlesChanged(titles.count());
+ }
+ updateStopPosition();
+ }
+
+
+ int MediaObject::_iface_availableTitles() const
+ {
+ return m_titles.count() - 1;
+ }
+
+ int MediaObject::_iface_currentTitle() const
+ {
+ return m_currentTitle;
+ }
+
+ void MediaObject::_iface_setCurrentTitle(int title, bool bseek)
+ {
+#ifdef GRAPH_DEBUG
+ qDebug() << "_iface_setCurrentTitle" << title;
+#endif
+ const int oldTitle = m_currentTitle;
+ m_currentTitle = title;
+ updateStopPosition();
+ if (bseek) {
+ //let's seek to the beginning of the song
+ seek(0);
+ } else {
+ updateTargetTick();
+ }
+ if (oldTitle != title) {
+ emit titleChanged(title);
+ emit totalTimeChanged(totalTime());
+ }
+
+ }
+
+ void MediaObject::updateStopPosition()
+ {
+ if (!m_autoplayTitles && m_currentTitle < _iface_availableTitles() - 1) {
+ //stop position is set to the end of the track
+ currentGraph()->setStopPosition(titleAbsolutePosition(m_currentTitle+1));
+ } else {
+ //stop position is set to the end
+ currentGraph()->setStopPosition(-1);
+ }
+ }
+#endif //QT_NO_PHONON_QT_NO_PHONON_MEDIACONTROLLER
+
+ void MediaObject::switchFilters(int index, Filter oldFilter, Filter newFilter)
+ {
+ if (currentGraph()->index() == index) {
+ currentGraph()->switchFilters(oldFilter, newFilter);
+ } else {
+ nextGraph()->switchFilters(oldFilter, newFilter);
+ }
+
+ }
+
+
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_mediaobject.cpp"
diff --git a/src/3rdparty/phonon/ds9/mediaobject.h b/src/3rdparty/phonon/ds9/mediaobject.h
new file mode 100644
index 0000000..2c34ffc
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/mediaobject.h
@@ -0,0 +1,313 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_MEDIAOBJECT_H
+#define PHONON_MEDIAOBJECT_H
+
+#include <phonon/mediaobjectinterface.h>
+#include <phonon/addoninterface.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QObject>
+#include <QtCore/QQueue>
+#include <QtCore/QBasicTimer>
+#include <QtCore/QMutex>
+#include <QtCore/QThread>
+
+#include "backendnode.h"
+#include "mediagraph.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ class MediaSource;
+
+ namespace DS9
+ {
+ class VideoWidget;
+ class AudioOutput;
+
+ class QWinWaitCondition
+ {
+ public:
+ QWinWaitCondition() : m_handle(::CreateEvent(0,0,0,0))
+ {
+ }
+
+ ~QWinWaitCondition()
+ {
+ ::CloseHandle(m_handle);
+ }
+
+ void reset()
+ {
+ //will block
+ ::ResetEvent(m_handle);
+ }
+
+ void set()
+ {
+ //will unblock
+ ::SetEvent(m_handle);
+ }
+
+ operator HANDLE()
+ {
+ return m_handle;
+ }
+
+ operator HEVENT()
+ {
+ return reinterpret_cast<HEVENT>(m_handle);
+ }
+
+
+ private:
+ HANDLE m_handle;
+ };
+
+ class WorkerThread : public QThread
+ {
+ Q_OBJECT
+ public:
+ WorkerThread();
+ ~WorkerThread();
+
+ virtual void run();
+
+ //wants to know as soon as the state is set
+ void addStateChangeRequest(Graph graph, OAFilterState, QList<Filter> = QList<Filter>());
+
+ quint16 addSeekRequest(Graph graph, qint64 time);
+ quint16 addUrlToRender(const QString &url);
+ quint16 addFilterToRender(const Filter &filter);
+
+ void replaceGraphForEventManagement(Graph newGraph, Graph oldGraph);
+
+ void abortCurrentRender(qint16 renderId);
+
+ //tells the thread to stop processing
+ void signalStop();
+
+ Q_SIGNALS:
+ void asyncRenderFinished(quint16, HRESULT, Graph);
+ void asyncSeekingFinished(quint16, qint64);
+ void stateReady(Graph, Phonon::State);
+ void eventReady(Graph, long eventCode, long param1);
+
+ private:
+
+ enum Task
+ {
+ Render,
+ Seek,
+ ChangeState,
+ ReplaceGraph //just updates recalls WaitForMultipleObject
+ };
+
+ struct Work
+ {
+ Task task;
+ quint16 id;
+ Graph graph;
+ Graph oldGraph;
+ Filter filter;
+ QString url;
+ union
+ {
+ qint64 time;
+ OAFilterState state;
+ };
+ QList<Filter> decoders; //for the state change requests
+ };
+ Work dequeueWork();
+ void handleTask();
+
+ Graph m_currentRender;
+ qint16 m_currentRenderId;
+ QQueue<Work> m_queue;
+ bool m_finished;
+ quint16 m_currentWorkId;
+ QWinWaitCondition m_waitCondition;
+ QMutex m_mutex;
+
+ //this is for WaitForMultipleObjects
+ struct
+ {
+ Graph graph;
+ HANDLE handle;
+ } m_graphHandle[FILTER_COUNT];
+ };
+
+
+ class MediaObject : public BackendNode, public Phonon::MediaObjectInterface
+#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ , public Phonon::AddonInterface
+#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ {
+ friend class Stream;
+ Q_OBJECT
+ Q_INTERFACES(Phonon::MediaObjectInterface
+#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ Phonon::AddonInterface
+#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ )
+ public:
+ MediaObject(QObject *parent);
+ ~MediaObject();
+ Phonon::State state() const;
+ bool hasVideo() const;
+ bool isSeekable() const;
+ qint64 currentTime() const;
+ qint32 tickInterval() const;
+
+ void setTickInterval(qint32 newTickInterval);
+ void play();
+ void pause();
+ void stop();
+ void ensureStopped();
+ void seek(qint64 time);
+
+ QString errorString() const;
+ Phonon::ErrorType errorType() const;
+
+#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ bool hasInterface(Interface) const;
+ QVariant interfaceCall(Interface iface, int command, const QList<QVariant> &params);
+#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
+
+ qint64 totalTime() const;
+ qint32 prefinishMark() const;
+ void setPrefinishMark(qint32 newPrefinishMark);
+
+ qint32 transitionTime() const;
+ void setTransitionTime(qint32);
+
+ qint64 remainingTime() const;
+
+ MediaSource source() const;
+ void setSource(const MediaSource &source);
+ void setNextSource(const MediaSource &source);
+
+
+ //COM error management
+ bool catchComError(HRESULT hr);
+
+ void grabNode(BackendNode *node);
+ bool connectNodes(BackendNode *source, BackendNode *sink);
+ bool disconnectNodes(BackendNode *source, BackendNode *sink);
+
+ void switchFilters(int index, Filter oldFilter, Filter newFilter);
+
+ WorkerThread *workerThread();
+ void loadingFinished(MediaGraph *mg);
+ void seekingFinished(MediaGraph *mg);
+ MediaGraph *currentGraph() const;
+
+ //this is used by the backend only
+ Phonon::State transactionState;
+
+ private Q_SLOTS:
+ void switchToNextSource();
+ void slotStateReady(Graph, Phonon::State);
+ void handleEvents(Graph, long eventCode, long param1);
+ void finishLoading(quint16 workId, HRESULT hr, Graph);
+ void finishSeeking(quint16 workId, qint64 time);
+
+ Q_SIGNALS:
+ void stateChanged(Phonon::State newstate, Phonon::State oldstate);
+ void tick(qint64 time);
+ void metaDataChanged(QMultiMap<QString, QString>);
+ void seekableChanged(bool);
+ void hasVideoChanged(bool);
+ void bufferStatus(int);
+
+ // AddonInterface:
+ void titleChanged(int);
+ void availableTitlesChanged(int);
+ void chapterChanged(int);
+ void availableChaptersChanged(int);
+ void angleChanged(int);
+ void availableAnglesChanged(int);
+
+ void finished();
+ void prefinishMarkReached(qint32);
+ void aboutToFinish();
+ void totalTimeChanged(qint64 length) const;
+ void currentSourceChanged(const MediaSource &);
+
+ protected:
+ void setState(Phonon::State);
+ void timerEvent(QTimerEvent *e);
+
+ private:
+#ifndef QT_NO_PHONON_VIDEO
+ void updateVideoGeometry();
+#endif // QT_NO_PHONON_VIDEO
+ void handleComplete(IGraphBuilder *graph);
+ MediaGraph *nextGraph() const;
+
+ void updateTargetTick();
+ void updateStopPosition();
+
+ mutable QString m_errorString;
+ mutable Phonon::ErrorType m_errorType;
+
+ Phonon::State m_state;
+ Phonon::State m_nextState;
+ qint32 m_transitionTime;
+
+ qint32 m_prefinishMark;
+
+ QBasicTimer m_tickTimer;
+ qint32 m_tickInterval;
+
+ //the graph(s)
+ MediaGraph* m_graphs[FILTER_COUNT];
+
+ //...the videowidgets in the graph
+ QList<VideoWidget*> m_videoWidgets;
+ QList<AudioOutput*> m_audioOutputs;
+
+ bool m_buffering:1;
+ bool m_oldHasVideo:1;
+ bool m_prefinishMarkSent:1;
+ bool m_aboutToFinishSent:1;
+ bool m_nextSourceReadyToStart:1;
+
+ //for TitleInterface (and commands)
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ bool m_autoplayTitles:1;
+ QList<qint64> m_titles;
+ int m_currentTitle;
+ int _iface_availableTitles() const;
+ int _iface_currentTitle() const;
+ void _iface_setCurrentTitle(int title, bool bseek = true);
+ void setTitles(const QList<qint64> &titles);
+ qint64 titleAbsolutePosition(int title) const;
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+ qint64 m_targetTick;
+
+ WorkerThread m_thread;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // PHONON_MEDIAOBJECT_H
diff --git a/src/3rdparty/phonon/ds9/phononds9_namespace.h b/src/3rdparty/phonon/ds9/phononds9_namespace.h
new file mode 100644
index 0000000..e972d41
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/phononds9_namespace.h
@@ -0,0 +1,33 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_DS9_NAMESPACE_H
+#define PHONON_DS9_NAMESPACE_H
+
+#include <QtCore/qnamespace.h>
+
+#define FILTER_COUNT 2 //number of ds9 filters per object
+
+#ifndef QT_BEGIN_NAMESPACE
+#define QT_BEGIN_NAMESPACE
+#endif
+
+#ifndef QT_END_NAMESPACE
+#define QT_END_NAMESPACE
+#endif
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/qasyncreader.cpp b/src/3rdparty/phonon/ds9/qasyncreader.cpp
new file mode 100644
index 0000000..68ec1f8
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qasyncreader.cpp
@@ -0,0 +1,198 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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/QFile>
+
+#include "qasyncreader.h"
+#include "qbasefilter.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ QAsyncReader::QAsyncReader(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mediaTypes) : QPin(parent, PINDIR_OUTPUT, mediaTypes)
+ {
+ }
+
+ QAsyncReader::~QAsyncReader()
+ {
+ }
+
+ STDMETHODIMP QAsyncReader::QueryInterface(REFIID iid, void **out)
+ {
+ if (!out) {
+ return E_POINTER;
+ }
+
+ if (iid == IID_IAsyncReader) {
+ AddRef();
+ *out = static_cast<IAsyncReader*>(this);
+ return S_OK;
+ }
+
+ return QPin::QueryInterface(iid, out);
+ }
+
+ STDMETHODIMP_(ULONG) QAsyncReader::AddRef()
+ {
+ return QPin::AddRef();
+ }
+
+ STDMETHODIMP_(ULONG) QAsyncReader::Release()
+ {
+ return QPin::Release();
+ }
+
+
+ STDMETHODIMP QAsyncReader::RequestAllocator(IMemAllocator *preferred, ALLOCATOR_PROPERTIES *prop,IMemAllocator **actual)
+ {
+ ALLOCATOR_PROPERTIES prop2;
+
+ if (prop->cbAlign == 0) {
+ prop->cbAlign = 1; //align on 1 char
+ }
+
+ if (preferred && preferred->SetProperties(prop, &prop2) == S_OK) {
+ preferred->AddRef();
+ *actual = preferred;
+ return S_OK;
+ }
+
+ //we should try to create one memory allocator ourselves here
+ return E_FAIL;
+ }
+
+ STDMETHODIMP QAsyncReader::Request(IMediaSample *sample,DWORD_PTR user)
+ {
+ QMutexLocker mutexLocker(&m_mutexWait);
+ QWriteLocker locker(&m_lock);
+ if (m_flushing) {
+ return VFW_E_WRONG_STATE;
+ }
+
+ m_requestQueue.enqueue(AsyncRequest(sample, user));
+ m_requestWait.wakeOne();
+ return S_OK;
+ }
+
+ STDMETHODIMP QAsyncReader::WaitForNext(DWORD timeout, IMediaSample **sample, DWORD_PTR *user)
+ {
+ QMutexLocker locker(&m_mutexWait);
+ if (!sample ||!user) {
+ return E_POINTER;
+ }
+
+ *sample = 0;
+ *user = 0;
+
+ AsyncRequest r = getNextRequest();
+
+ if (r.sample == 0) {
+ //there is no request in the queue
+ if (isFlushing()) {
+ return VFW_E_WRONG_STATE;
+ } else {
+ //First we need to lock the mutex
+ if (m_requestWait.wait(&m_mutexWait, timeout) == false) {
+ return VFW_E_TIMEOUT;
+ }
+ if (isFlushing()) {
+ return VFW_E_WRONG_STATE;
+ }
+
+ r = getNextRequest();
+ }
+ }
+
+ //at this point we're sure to have a request to proceed
+ if (r.sample == 0) {
+ return E_FAIL;
+ }
+
+ *sample = r.sample;
+ *user = r.user;
+
+ return SyncReadAligned(r.sample);
+ }
+
+ STDMETHODIMP QAsyncReader::BeginFlush()
+ {
+ QMutexLocker mutexLocker(&m_mutexWait);
+ QWriteLocker locker(&m_lock);
+ m_flushing = true;
+ m_requestWait.wakeOne();
+ return S_OK;
+ }
+
+ STDMETHODIMP QAsyncReader::EndFlush()
+ {
+ QWriteLocker locker(&m_lock);
+ m_flushing = false;
+ return S_OK;
+ }
+
+ STDMETHODIMP QAsyncReader::SyncReadAligned(IMediaSample *sample)
+ {
+ if (!sample) {
+ return E_POINTER;
+ }
+
+ REFERENCE_TIME start = 0,
+ stop = 0;
+ HRESULT hr = sample->GetTime(&start, &stop);
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ LONGLONG startPos = start / 10000000;
+ LONG length = static_cast<LONG>((stop - start) / 10000000);
+
+ BYTE *buffer;
+ hr = sample->GetPointer(&buffer);
+ if(FAILED(hr)) {
+ return hr;
+ }
+
+ LONG actual = 0;
+ read(startPos, length, buffer, &actual);
+
+ return sample->SetActualDataLength(actual);
+ }
+
+ STDMETHODIMP QAsyncReader::SyncRead(LONGLONG pos, LONG length, BYTE *buffer)
+ {
+ return read(pos, length, buffer, 0);
+ }
+
+
+ //addition
+ QAsyncReader::AsyncRequest QAsyncReader::getNextRequest()
+ {
+ QWriteLocker locker(&m_lock);
+ AsyncRequest ret;
+ if (!m_requestQueue.isEmpty()) {
+ ret = m_requestQueue.dequeue();
+ }
+
+ return ret;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/qasyncreader.h b/src/3rdparty/phonon/ds9/qasyncreader.h
new file mode 100644
index 0000000..cb789ee
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qasyncreader.h
@@ -0,0 +1,77 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_QASYNCREADER_H
+#define PHONON_QASYNCREADER_H
+
+#include <QtCore/QWaitCondition>
+#include <QtCore/QQueue>
+#include <QtCore/QMutex>
+
+#include "qpin.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ //his class reads asynchronously from a QIODevice
+ class QAsyncReader : public QPin, public IAsyncReader
+ {
+ public:
+ QAsyncReader(QBaseFilter *, const QVector<AM_MEDIA_TYPE> &mediaTypes);
+ ~QAsyncReader();
+
+ //reimplementation from IUnknown
+ STDMETHODIMP QueryInterface(REFIID iid, void** out);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ //reimplementation from IAsyncReader
+ STDMETHODIMP RequestAllocator(IMemAllocator *,ALLOCATOR_PROPERTIES *,IMemAllocator **);
+ STDMETHODIMP Request(IMediaSample *,DWORD_PTR);
+ STDMETHODIMP WaitForNext(DWORD,IMediaSample **,DWORD_PTR *);
+ STDMETHODIMP SyncReadAligned(IMediaSample *);
+ STDMETHODIMP SyncRead(LONGLONG,LONG,BYTE *);
+ virtual STDMETHODIMP Length(LONGLONG *,LONGLONG *) = 0;
+ STDMETHODIMP BeginFlush();
+ STDMETHODIMP EndFlush();
+
+ protected:
+ virtual HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual) = 0;
+
+ private:
+ struct AsyncRequest
+ {
+ AsyncRequest(IMediaSample *s = 0, DWORD_PTR u = 0) : sample(s), user(u) {}
+ IMediaSample *sample;
+ DWORD_PTR user;
+ };
+ AsyncRequest getNextRequest();
+
+ QMutex m_mutexWait;
+
+ QQueue<AsyncRequest> m_requestQueue;
+ QWaitCondition m_requestWait;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/qaudiocdreader.cpp b/src/3rdparty/phonon/ds9/qaudiocdreader.cpp
new file mode 100644
index 0000000..b9f9fd6
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qaudiocdreader.cpp
@@ -0,0 +1,332 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "qaudiocdreader.h"
+#include <dshow.h>
+#include <initguid.h>
+
+#include <winioctl.h> // needed for FILE_DEVICE_CD_ROM etc
+
+#define IOCTL_CDROM_READ_TOC CTL_CODE(FILE_DEVICE_CD_ROM, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_RAW_READ CTL_CODE(FILE_DEVICE_CD_ROM, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS)
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ // {CA46BFE1-D55B-4adf-B803-BC2B9AD57824}
+ DEFINE_GUID(IID_ITitleInterface,
+ 0xca46bfe1, 0xd55b, 0x4adf, 0xb8, 0x3, 0xbc, 0x2b, 0x9a, 0xd5, 0x78, 0x24);
+
+ struct TRACK_DATA {
+ UCHAR Reserved;
+ UCHAR Control : 4;
+ UCHAR Adr : 4;
+ UCHAR TrackNumber;
+ UCHAR Reserved1;
+ UCHAR Address[4];
+ };
+
+ struct CDROM_TOC {
+ UCHAR Length[2];
+ UCHAR FirstTrack;
+ UCHAR LastTrack;
+ TRACK_DATA TrackData[100];
+ };
+
+ struct WaveStructure
+ {
+ WaveStructure();
+
+ char riff[4];
+ qint32 chunksize;
+ char wave[4];
+ char fmt[4];
+ const qint32 chunksize2;
+ const quint16 formatTag;
+ const quint16 nChannels;
+ const quint32 nSamplesPerSec;
+ const quint32 nAvgBytesPerSec;
+ const quint16 nBlockAlign;
+ const quint16 bitsPerSample;
+ char data[4];
+ qint32 dataLength;
+ };
+
+ enum TRACK_MODE_TYPE {
+ YellowMode2,
+ XAForm2,
+ CDDA
+ };
+
+
+ struct RAW_READ_INFO {
+ LARGE_INTEGER DiskOffset;
+ ULONG SectorCount;
+ TRACK_MODE_TYPE TrackMode;
+ };
+
+ class QAudioCDReader : public QAsyncReader, public ITitleInterface
+ {
+ public:
+ QAudioCDReader(QBaseFilter *parent, QChar drive = QChar());
+ ~QAudioCDReader();
+
+ //reimplementation from IUnknown
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ STDMETHODIMP Length(LONGLONG *,LONGLONG *);
+ STDMETHODIMP QueryInterface(REFIID iid, void** out);
+ QList<qint64> titles() const;
+
+ protected:
+ HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual);
+
+ private:
+ HANDLE m_cddrive;
+ CDROM_TOC *m_toc;
+ WaveStructure *m_waveHeader;
+ qint64 m_trackAddress;
+ };
+
+
+#define SECTOR_SIZE 2352
+#define NB_SECTORS_READ 20
+
+ static AM_MEDIA_TYPE getAudioCDMediaType()
+ {
+ AM_MEDIA_TYPE mt;
+ qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE));
+ mt.majortype = MEDIATYPE_Stream;
+ mt.subtype = MEDIASUBTYPE_WAVE;
+ mt.bFixedSizeSamples = TRUE;
+ mt.bTemporalCompression = FALSE;
+ mt.lSampleSize = 1;
+ mt.formattype = GUID_NULL;
+ return mt;
+ }
+
+ int addressToSectors(UCHAR address[4])
+ {
+ return ((address[0] * 60 + address[1]) * 60 + address[2]) * 75 + address[3] - 150;
+ }
+
+ WaveStructure::WaveStructure() : chunksize(0), chunksize2(16),
+ formatTag(WAVE_FORMAT_PCM), nChannels(2), nSamplesPerSec(44100), nAvgBytesPerSec(176400), nBlockAlign(4), bitsPerSample(16),
+ dataLength(0)
+ {
+ qMemCopy(riff, "RIFF", 4);
+ qMemCopy(wave, "WAVE", 4);
+ qMemCopy(fmt, "fmt ", 4);
+ qMemCopy(data, "data", 4);
+ }
+
+
+ QAudioCDReader::QAudioCDReader(QBaseFilter *parent, QChar drive) : QAsyncReader(parent, QVector<AM_MEDIA_TYPE>() << getAudioCDMediaType())
+ {
+ m_toc = new CDROM_TOC;
+ m_waveHeader = new WaveStructure;
+
+ //now open the cd-drive
+ QString path;
+ if (drive.isNull()) {
+ path = QString::fromLatin1("\\\\.\\Cdrom0");
+ } else {
+ path = QString::fromLatin1("\\\\.\\%1:").arg(drive);
+ }
+
+ m_cddrive = QT_WA_INLINE (
+ ::CreateFile( (TCHAR*)path.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ),
+ ::CreateFileA( path.toLocal8Bit().constData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL )
+ );
+
+ qMemSet(m_toc, 0, sizeof(CDROM_TOC));
+ //read the TOC
+ DWORD bytesRead = 0;
+ bool tocRead = ::DeviceIoControl(m_cddrive, IOCTL_CDROM_READ_TOC, 0, 0, m_toc, sizeof(CDROM_TOC), &bytesRead, 0);
+
+ if (!tocRead) {
+ qWarning("unable to load the TOC from the CD");
+ return;
+ }
+
+ m_trackAddress = addressToSectors(m_toc->TrackData[0].Address);
+ const qint32 nbSectorsToRead = (addressToSectors(m_toc->TrackData[m_toc->LastTrack + 1 - m_toc->FirstTrack].Address)
+ - m_trackAddress);
+ const qint32 dataLength = nbSectorsToRead * SECTOR_SIZE;
+
+ m_waveHeader->chunksize = 4 + (8 + m_waveHeader->chunksize2) + (8 + dataLength);
+ m_waveHeader->dataLength = dataLength;
+ }
+
+ QAudioCDReader::~QAudioCDReader()
+ {
+ ::CloseHandle(m_cddrive);
+ delete m_toc;
+ delete m_waveHeader;
+
+ }
+
+ STDMETHODIMP_(ULONG) QAudioCDReader::AddRef()
+ {
+ return QAsyncReader::AddRef();
+ }
+
+ STDMETHODIMP_(ULONG) QAudioCDReader::Release()
+ {
+ return QAsyncReader::Release();
+ }
+
+
+ STDMETHODIMP QAudioCDReader::Length(LONGLONG *total,LONGLONG *available)
+ {
+ const LONGLONG length = sizeof(WaveStructure) + m_waveHeader->dataLength;
+ if (total) {
+ *total = length;
+ }
+ if (available) {
+ *available = length;
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP QAudioCDReader::QueryInterface(REFIID iid, void** out)
+ {
+ if (!out) {
+ return E_POINTER;
+ }
+
+ if (iid == IID_ITitleInterface) {
+ //we reroute that to the pin
+ *out = static_cast<ITitleInterface*>(this);
+ AddRef();
+ return S_OK;
+ } else {
+ return QAsyncReader::QueryInterface(iid, out);
+ }
+ }
+
+
+ HRESULT QAudioCDReader::read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual)
+ {
+ LONG nbRead = 0;
+
+ if (actual) {
+ *actual = 0;
+ }
+
+ if (pos < sizeof(WaveStructure)) {
+ //we first copy the content of the structure
+ nbRead = qMin(LONG(sizeof(WaveStructure) - pos), length);
+ qMemCopy(buffer, reinterpret_cast<char*>(m_waveHeader) + pos, nbRead);
+ }
+
+ const LONGLONG posInTrack = pos - sizeof(WaveStructure) + nbRead;
+ const int bytesLeft = qMin(m_waveHeader->dataLength - posInTrack, LONGLONG(length - nbRead));
+
+ if (bytesLeft > 0) {
+
+ //we need to read again
+
+ const int surplus = posInTrack % SECTOR_SIZE; //how many bytes too much at the beginning
+ const int firstSector = posInTrack / SECTOR_SIZE,
+ lastSector = (posInTrack + length - 1) / SECTOR_SIZE;
+ const int sectorsNeeded = lastSector - firstSector + 1;
+ int sectorsRead = 0;
+
+ QByteArray ba(sectorsNeeded * SECTOR_SIZE, 0);
+
+
+ RAW_READ_INFO ReadInfo;
+ ReadInfo.TrackMode = CDDA; // Always use CDDA (numerical: 2)
+ ReadInfo.DiskOffset.QuadPart = (m_trackAddress + firstSector) * 2048;
+ ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ);
+ while (ReadInfo.SectorCount) {
+ DWORD dummy = 0;
+ if (::DeviceIoControl( m_cddrive, IOCTL_CDROM_RAW_READ,
+ &ReadInfo, sizeof(ReadInfo),
+ ba.data() + sectorsRead * SECTOR_SIZE,
+ ReadInfo.SectorCount * SECTOR_SIZE,
+ &dummy, NULL ) )
+ {
+ ReadInfo.DiskOffset.QuadPart += ReadInfo.SectorCount * 2048;
+ sectorsRead += ReadInfo.SectorCount;
+ ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ);
+ }else {
+ qWarning("an error occurred while reading from the media");
+ return S_FALSE;
+ }
+
+ }
+
+ //consume bytes on the buffer
+ qMemCopy(buffer + nbRead, ba.data() + surplus, bytesLeft);
+
+ //at this point we have all we need in the buffer
+ nbRead += bytesLeft;
+ }
+
+ if (actual) {
+ *actual = nbRead;
+ }
+
+ return nbRead == length ? S_OK : S_FALSE;
+ }
+
+ QList<qint64> QAudioCDReader::titles() const
+ {
+ QList<qint64> ret;
+ ret << 0;
+ for(int i = m_toc->FirstTrack; i <= m_toc->LastTrack ; ++i) {
+ const uchar *address = m_toc->TrackData[i].Address;
+ ret << ((address[0] * 60 + address[1]) * 60 + address[2]) * 1000 + address[3]*1000/75 - 2000;
+
+ }
+ return ret;
+ }
+
+
+ QAudioCDPlayer::QAudioCDPlayer() : QBaseFilter(CLSID_NULL)
+ {
+ new QAudioCDReader(this);
+ }
+
+ QAudioCDPlayer::~QAudioCDPlayer()
+ {
+ }
+
+ STDMETHODIMP QAudioCDPlayer::QueryInterface(REFIID iid, void** out)
+ {
+ if (iid == IID_ITitleInterface) {
+ //we reroute that to the pin
+ return pins().first()->QueryInterface(iid, out);
+ } else {
+ return QBaseFilter::QueryInterface(iid, out);
+ }
+ }
+ }
+}
+
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/qaudiocdreader.h b/src/3rdparty/phonon/ds9/qaudiocdreader.h
new file mode 100644
index 0000000..eff845d
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qaudiocdreader.h
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_QAUDIOCDREADER_H
+#define PHONON_QAUDIOCDREADER_H
+
+#include "qasyncreader.h"
+#include "qbasefilter.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ struct CDROM_TOC;
+ struct WaveStructure;
+ EXTERN_C const IID IID_ITitleInterface;
+
+ //interface for the Titles
+ struct ITitleInterface : public IUnknown
+ {
+ virtual QList<qint64> titles() const = 0;
+ };
+
+
+ class QAudioCDPlayer : public QBaseFilter
+ {
+ public:
+ QAudioCDPlayer();
+ ~QAudioCDPlayer();
+ STDMETHODIMP QueryInterface(REFIID iid, void** out);
+ };
+
+ }
+}
+
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/qbasefilter.cpp b/src/3rdparty/phonon/ds9/qbasefilter.cpp
new file mode 100644
index 0000000..95cab92
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qbasefilter.cpp
@@ -0,0 +1,831 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "qbasefilter.h"
+#include "qpin.h"
+
+#include <QtCore/QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+
+ class QEnumPins : public IEnumPins
+ {
+ public:
+ QEnumPins(QBaseFilter *filter) : m_refCount(1),
+ m_filter(filter), m_pins(filter->pins()), m_index(0)
+ {
+ m_filter->AddRef();
+ }
+
+ virtual ~QEnumPins()
+ {
+ m_filter->Release();
+ }
+
+ STDMETHODIMP QueryInterface(const IID &iid,void **out)
+ {
+ if (!out) {
+ return E_POINTER;
+ }
+
+ HRESULT hr = S_OK;
+ if (iid == IID_IEnumPins) {
+ *out = static_cast<IEnumPins*>(this);
+ } else if (iid == IID_IUnknown) {
+ *out = static_cast<IUnknown*>(this);
+ } else {
+ *out = 0;
+ hr = E_NOINTERFACE;
+ }
+
+ if (S_OK)
+ AddRef();
+ return hr;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef()
+ {
+ return InterlockedIncrement(&m_refCount);
+ }
+
+ STDMETHODIMP_(ULONG) Release()
+ {
+ ULONG refCount = InterlockedDecrement(&m_refCount);
+ if (refCount == 0) {
+ delete this;
+ }
+
+ return refCount;
+ }
+
+ STDMETHODIMP Next(ULONG count,IPin **ret,ULONG *fetched)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_filter->pins() != m_pins) {
+ return VFW_E_ENUM_OUT_OF_SYNC;
+ }
+
+ if (fetched == 0 && count > 1) {
+ return E_INVALIDARG;
+ }
+
+ if (!ret) {
+ return E_POINTER;
+ }
+
+ int nbfetched = 0;
+ while (nbfetched < int(count) && m_index < m_pins.count()) {
+ IPin *current = m_pins[m_index];
+ current->AddRef();
+ ret[nbfetched] = current;
+ nbfetched++;
+ m_index++;
+ }
+
+ if (fetched) {
+ *fetched = nbfetched;
+ }
+
+ return nbfetched == count ? S_OK : S_FALSE;
+ }
+
+ STDMETHODIMP Skip(ULONG count)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_filter->pins() != m_pins) {
+ return VFW_E_ENUM_OUT_OF_SYNC;
+ }
+
+ m_index = qMin(m_index + int(count), m_pins.count());
+ return m_index == m_pins.count() ? S_FALSE : S_OK;
+ }
+
+ STDMETHODIMP Reset()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_index = 0;
+ return S_OK;
+ }
+
+ STDMETHODIMP Clone(IEnumPins **out)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_filter->pins() != m_pins) {
+ return VFW_E_ENUM_OUT_OF_SYNC;
+ }
+
+ if (!out) {
+ return E_POINTER;
+ }
+
+ *out = new QEnumPins(m_filter);
+ (*out)->Skip(m_index);
+ return S_OK;
+ }
+
+
+ private:
+ LONG m_refCount;
+ QBaseFilter *m_filter;
+ QList<QPin*> m_pins;
+ int m_index;
+ QMutex m_mutex;
+ };
+
+
+ QBaseFilter::QBaseFilter(const CLSID &clsid):
+ m_refCount(1), m_clsid(clsid), m_clock(0), m_graph(0), m_state(State_Stopped)
+ {
+ }
+
+ QBaseFilter::~QBaseFilter()
+ {
+ while (!m_pins.isEmpty()) {
+ delete m_pins.first();
+ }
+ }
+
+ const QList<QPin *> QBaseFilter::pins() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_pins;
+ }
+
+ void QBaseFilter::addPin(QPin *pin)
+ {
+ QWriteLocker locker(&m_lock);
+ m_pins.append(pin);
+ }
+
+ void QBaseFilter::removePin(QPin *pin)
+ {
+ QWriteLocker locker(&m_lock);
+ m_pins.removeAll(pin);
+ }
+
+ FILTER_STATE QBaseFilter::state() const
+ {
+ return m_state;
+ }
+
+ IFilterGraph *QBaseFilter::graph() const
+ {
+ return m_graph;
+ }
+
+ STDMETHODIMP QBaseFilter::QueryInterface(REFIID iid, void **out)
+ {
+ if (!out) {
+ return E_POINTER;
+ }
+
+ HRESULT hr = S_OK;
+
+ if (iid == IID_IBaseFilter) {
+ *out = static_cast<IBaseFilter*>(this);
+ } else if (iid == IID_IMediaFilter) {
+ *out = static_cast<IMediaFilter*>(this);
+ } else if (iid == IID_IPersist) {
+ *out = static_cast<IPersist*>(this);
+ } else if (iid == IID_IUnknown) {
+ *out = static_cast<IUnknown*>(static_cast<IBaseFilter*>(this));
+ }
+ else if (iid == IID_IMediaPosition || iid == IID_IMediaSeeking) {
+ if (inputPins().isEmpty()) {
+ if (*out = getUpStreamInterface(iid)) {
+ return S_OK; //we return here to avoid adding a reference
+ } else {
+ hr = E_NOINTERFACE;
+ }
+ } else if (iid == IID_IMediaSeeking) {
+ *out = static_cast<IMediaSeeking*>(this);
+ } else if (iid == IID_IMediaPosition ||iid == IID_IDispatch) {
+ *out = static_cast<IMediaPosition*>(this);
+ }
+ } else {
+ *out = 0;
+ hr = E_NOINTERFACE;
+ }
+
+ if (hr == S_OK) {
+ AddRef();
+ }
+
+ return hr;
+ }
+
+ STDMETHODIMP_(ULONG) QBaseFilter::AddRef()
+ {
+ return InterlockedIncrement(&m_refCount);
+ }
+
+ STDMETHODIMP_(ULONG) QBaseFilter::Release()
+ {
+ ULONG refCount = InterlockedDecrement(&m_refCount);
+ if (refCount == 0) {
+ delete this;
+ }
+
+ return refCount;
+ }
+
+ STDMETHODIMP QBaseFilter::GetClassID(CLSID *clsid)
+ {
+ QReadLocker locker(&m_lock);
+ *clsid = m_clsid;
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::Stop()
+ {
+ QWriteLocker locker(&m_lock);
+ m_state = State_Stopped;
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::Pause()
+ {
+ QWriteLocker locker(&m_lock);
+ m_state = State_Paused;
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::Run(REFERENCE_TIME)
+ {
+ QWriteLocker locker(&m_lock);
+ m_state = State_Running;
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::GetState(DWORD, FILTER_STATE *state)
+ {
+ QReadLocker locker(&m_lock);
+ if (!state) {
+ return E_POINTER;
+ }
+
+ *state = m_state;
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::SetSyncSource(IReferenceClock *clock)
+ {
+ QWriteLocker locker(&m_lock);
+ if (clock) {
+ clock->AddRef();
+ }
+ if (m_clock) {
+ m_clock->Release();
+ }
+ m_clock = clock;
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::GetSyncSource(IReferenceClock **clock)
+ {
+ QReadLocker locker(&m_lock);
+ if (!clock) {
+ return E_POINTER;
+ }
+
+ if (m_clock) {
+ m_clock->AddRef();
+ }
+
+ *clock = m_clock;
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::FindPin(LPCWSTR name, IPin**pin)
+ {
+ if (!pin) {
+ return E_POINTER;
+ }
+
+ for (int i = 0; i < m_pins.count(); ++i) {
+ IPin * current = m_pins.at(i);
+ PIN_INFO info;
+ current->QueryPinInfo(&info);
+ if (info.pFilter) {
+ info.pFilter->Release();
+ }
+ if ( wcscmp(info.achName, name) == 0) {
+ *pin = current;
+ current->AddRef();
+ return S_OK;
+ }
+ }
+
+ *pin = 0;
+ return VFW_E_NOT_FOUND;
+ }
+
+ STDMETHODIMP QBaseFilter::QueryFilterInfo(FILTER_INFO *info )
+ {
+ QReadLocker locker(&m_lock);
+ if (!info) {
+ return E_POINTER;
+ }
+ info->pGraph = m_graph;
+ if (m_graph) {
+ m_graph->AddRef();
+ }
+ qMemCopy(info->achName, m_name.utf16(), qMin(MAX_FILTER_NAME, m_name.length()+1) *2);
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::JoinFilterGraph(IFilterGraph *graph, LPCWSTR name)
+ {
+ QWriteLocker locker(&m_lock);
+ m_graph = graph;
+ m_name = QString::fromUtf16((const unsigned short*)name);
+ return S_OK;
+ }
+
+ STDMETHODIMP QBaseFilter::EnumPins( IEnumPins **ep)
+ {
+ if (!ep) {
+ return E_POINTER;
+ }
+
+ *ep = new QEnumPins(this);
+ return S_OK;
+ }
+
+
+ STDMETHODIMP QBaseFilter::QueryVendorInfo(LPWSTR *)
+ {
+ //we give no information on that
+ return E_NOTIMPL;
+ }
+
+ //implementation from IMediaSeeking
+ STDMETHODIMP QBaseFilter::GetCapabilities(DWORD *pCapabilities)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetCapabilities(pCapabilities);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::CheckCapabilities(DWORD *pCapabilities)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->CheckCapabilities(pCapabilities);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::IsFormatSupported(const GUID *pFormat)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->IsFormatSupported(pFormat);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::QueryPreferredFormat(GUID *pFormat)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->QueryPreferredFormat(pFormat);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetTimeFormat(GUID *pFormat)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetTimeFormat(pFormat);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::IsUsingTimeFormat(const GUID *pFormat)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->IsUsingTimeFormat(pFormat);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::SetTimeFormat(const GUID *pFormat)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->SetTimeFormat(pFormat);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetDuration(LONGLONG *pDuration)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetDuration(pDuration);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetStopPosition(LONGLONG *pStop)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetStopPosition(pStop);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetCurrentPosition(LONGLONG *pCurrent)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetCurrentPosition(pCurrent);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::ConvertTimeFormat(LONGLONG *pTarget,
+ const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->SetPositions(pCurrent, dwCurrentFlags, pStop, dwStopFlags);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetPositions(LONGLONG *pCurrent, LONGLONG *pStop)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetPositions(pCurrent, pStop);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetAvailable(pEarliest, pLatest);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::SetRate(double dRate)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->SetRate(dRate);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetRate(double *dRate)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetRate(dRate);
+ ms->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetPreroll(LONGLONG *pllPreroll)
+ {
+ IMediaSeeking *ms = getUpstreamMediaSeeking();
+ if (!ms) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = ms->GetPreroll(pllPreroll);
+ ms->Release();
+ return hr;
+ }
+
+ //implementation from IMediaPosition
+ STDMETHODIMP QBaseFilter::get_Duration(REFTIME *plength)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->get_Duration(plength);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::put_CurrentPosition(REFTIME llTime)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->put_CurrentPosition(llTime);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::get_CurrentPosition(REFTIME *pllTime)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->get_CurrentPosition(pllTime);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::get_StopTime(REFTIME *pllTime)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->get_StopTime(pllTime);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::put_StopTime(REFTIME llTime)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->put_StopTime(llTime);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::get_PrerollTime(REFTIME *pllTime)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->get_PrerollTime(pllTime);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::put_PrerollTime(REFTIME llTime)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->put_PrerollTime(llTime);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::put_Rate(double dRate)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->put_Rate(dRate);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::get_Rate(double *pdRate)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->get_Rate(pdRate);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::CanSeekForward(LONG *pCanSeekForward)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->CanSeekForward(pCanSeekForward);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::CanSeekBackward(LONG *pCanSeekBackward)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->CanSeekBackward(pCanSeekBackward);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetTypeInfoCount(UINT *pctinfo)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->GetTypeInfoCount(pctinfo);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->GetTypeInfo(iTInfo, lcid, ppTInfo);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
+ mp->Release();
+ return hr;
+ }
+
+ STDMETHODIMP QBaseFilter::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
+ VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
+ {
+ IMediaPosition *mp = getUpstreamMediaPosition();
+ if (!mp) {
+ return E_NOTIMPL;
+ }
+
+ HRESULT hr = mp->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+ mp->Release();
+ return hr;
+ }
+
+
+ IMediaSeeking *QBaseFilter::getUpstreamMediaSeeking()
+ {
+ return static_cast<IMediaSeeking*>(getUpStreamInterface(IID_IMediaSeeking));
+ }
+
+ IMediaPosition *QBaseFilter::getUpstreamMediaPosition()
+ {
+ return static_cast<IMediaPosition*>(getUpStreamInterface(IID_IMediaPosition));
+ }
+
+ QList<QPin*> QBaseFilter::inputPins() const
+ {
+ QList<QPin*> ret;
+ for(int i = 0; i < m_pins.count(); ++i) {
+ QPin * pin = m_pins.at(i);
+ if (pin->direction() == PINDIR_INPUT) {
+ ret += pin;
+ }
+ }
+ return ret;
+ }
+
+ QList<QPin*> QBaseFilter::outputPins() const
+ {
+ QList<QPin*> ret;
+ for(int i = 0; i < m_pins.count(); ++i) {
+ QPin * pin = m_pins.at(i);
+ if (pin->direction() == PINDIR_OUTPUT) {
+ ret += pin;
+ }
+ }
+ return ret;
+ }
+
+ void *QBaseFilter::getUpStreamInterface(const IID &iid) const
+ {
+ const QList<QPin*> inputs = inputPins();
+ for (int i = 0; i < inputs.count(); ++i) {
+ IPin *out = inputs.at(i)->connected();
+ if (out) {
+ void *ms = 0;
+ out->QueryInterface(iid, &ms);
+ if (ms) {
+ return ms;
+ }
+ }
+ }
+ //none was found
+ return 0;
+ }
+
+
+ //addition
+ HRESULT QBaseFilter::processSample(IMediaSample *)
+ {
+ return S_OK;
+ }
+
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/qbasefilter.h b/src/3rdparty/phonon/ds9/qbasefilter.h
new file mode 100644
index 0000000..85f1431
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qbasefilter.h
@@ -0,0 +1,136 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_QBASEFILTER_H
+#define PHONON_QBASEFILTER_H
+
+#include "phononds9_namespace.h"
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtCore/QReadWriteLock>
+
+#include <dshow.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class QPin;
+ class QBaseFilter : public IBaseFilter, public IMediaSeeking, public IMediaPosition
+ {
+ public:
+ QBaseFilter(const CLSID &clsid);
+ virtual ~QBaseFilter();
+
+ //implementation from IUnknown
+ STDMETHODIMP QueryInterface(REFIID iid, void** out);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ //implementation from IPersist
+ STDMETHODIMP GetClassID(CLSID *);
+
+ //implementation from IMediaFilter
+ STDMETHODIMP Stop();
+ STDMETHODIMP Pause();
+ STDMETHODIMP Run(REFERENCE_TIME);
+ STDMETHODIMP GetState(DWORD, FILTER_STATE*);
+ STDMETHODIMP SetSyncSource(IReferenceClock*);
+ STDMETHODIMP GetSyncSource(IReferenceClock**);
+
+ //implementation from IBaseFilter
+ STDMETHODIMP EnumPins(IEnumPins**);
+ STDMETHODIMP FindPin(LPCWSTR, IPin**);
+ STDMETHODIMP QueryFilterInfo(FILTER_INFO*);
+ STDMETHODIMP JoinFilterGraph(IFilterGraph*, LPCWSTR);
+ STDMETHODIMP QueryVendorInfo(LPWSTR*);
+
+ //implementation from IMediaSeeking
+ STDMETHODIMP GetCapabilities(DWORD *pCapabilities);
+ STDMETHODIMP CheckCapabilities(DWORD *pCapabilities);
+ STDMETHODIMP IsFormatSupported(const GUID *pFormat);
+ STDMETHODIMP QueryPreferredFormat(GUID *pFormat);
+ STDMETHODIMP GetTimeFormat(GUID *pFormat);
+ STDMETHODIMP IsUsingTimeFormat(const GUID *pFormat);
+ STDMETHODIMP SetTimeFormat(const GUID *pFormat);
+ STDMETHODIMP GetDuration(LONGLONG *pDuration);
+ STDMETHODIMP GetStopPosition(LONGLONG *pStop);
+ STDMETHODIMP GetCurrentPosition(LONGLONG *pCurrent);
+ STDMETHODIMP ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat);
+ STDMETHODIMP SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags);
+ STDMETHODIMP GetPositions(LONGLONG *pCurrent, LONGLONG *pStop);
+ STDMETHODIMP GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest);
+ STDMETHODIMP SetRate(double dRate);
+ STDMETHODIMP GetRate(double *dRate);
+ STDMETHODIMP GetPreroll(LONGLONG *pllPreroll);
+
+ //implementation from IMediaPosition
+ STDMETHODIMP get_Duration(REFTIME *plength);
+ STDMETHODIMP put_CurrentPosition(REFTIME llTime);
+ STDMETHODIMP get_CurrentPosition(REFTIME *pllTime);
+ STDMETHODIMP get_StopTime(REFTIME *pllTime);
+ STDMETHODIMP put_StopTime(REFTIME llTime);
+ STDMETHODIMP get_PrerollTime(REFTIME *pllTime);
+ STDMETHODIMP put_PrerollTime(REFTIME llTime);
+ STDMETHODIMP put_Rate(double dRate);
+ STDMETHODIMP get_Rate(double *pdRate);
+ STDMETHODIMP CanSeekForward(LONG *pCanSeekForward);
+ STDMETHODIMP CanSeekBackward(LONG *pCanSeekBackward);
+
+ //implementation from IDispatch (coming from IMediaPosition)
+ STDMETHODIMP GetTypeInfoCount(UINT *pctinfo);
+ STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
+ STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
+ STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
+ VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
+
+ //own methods
+ const QList<QPin *> pins() const;
+ void addPin(QPin *pin);
+ void removePin(QPin *pin);
+ IFilterGraph *graph() const;
+ FILTER_STATE state() const;
+
+
+ //reimplement this if you want specific processing of media sample
+ virtual HRESULT processSample(IMediaSample *);
+
+ private:
+ QList<QPin*> outputPins() const;
+ QList<QPin*> inputPins() const;
+
+ void *getUpStreamInterface(const IID &iid) const;
+ IMediaSeeking *getUpstreamMediaSeeking();
+ IMediaPosition *getUpstreamMediaPosition();
+
+ LONG m_refCount;
+ CLSID m_clsid;
+ QString m_name;
+ IReferenceClock *m_clock;
+ IFilterGraph *m_graph;
+ FILTER_STATE m_state;
+ QList<QPin *> m_pins;
+ mutable QReadWriteLock m_lock;
+ };
+ }
+}
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/qmeminputpin.cpp b/src/3rdparty/phonon/ds9/qmeminputpin.cpp
new file mode 100644
index 0000000..0af1bfd
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qmeminputpin.cpp
@@ -0,0 +1,357 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "qmeminputpin.h"
+#include "qbasefilter.h"
+#include "compointer.h"
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+
+ QMemInputPin::QMemInputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt, bool transform) :
+ QPin(parent, PINDIR_INPUT, mt), m_shouldDuplicateSamples(true), m_transform(transform)
+ {
+ }
+
+ QMemInputPin::~QMemInputPin()
+ {
+ }
+
+ STDMETHODIMP QMemInputPin::QueryInterface(REFIID iid, void **out)
+ {
+ if (!out) {
+ return E_POINTER;
+ }
+
+ if (iid == IID_IMemInputPin) {
+ *out = static_cast<IMemInputPin*>(this);
+ AddRef();
+ return S_OK;
+ } else {
+ return QPin::QueryInterface(iid, out);
+ }
+ }
+
+ STDMETHODIMP_(ULONG) QMemInputPin::AddRef()
+ {
+ return QPin::AddRef();
+ }
+
+ STDMETHODIMP_(ULONG) QMemInputPin::Release()
+ {
+ return QPin::Release();
+ }
+
+ STDMETHODIMP QMemInputPin::EndOfStream()
+ {
+ //this allows to serialize with Receive calls
+ QMutexLocker locker(&m_mutexReceive);
+ for(int i = 0; i < m_outputs.count(); ++i) {
+ IPin *conn = m_outputs.at(i)->connected();
+ if (conn) {
+ conn->EndOfStream();
+ }
+ }
+ return S_OK;
+ }
+
+ STDMETHODIMP QMemInputPin::BeginFlush()
+ {
+ //pass downstream
+ for(int i = 0; i < m_outputs.count(); ++i) {
+ IPin *conn = m_outputs.at(i)->connected();
+ if (conn) {
+ conn->BeginFlush();
+ }
+ }
+ QWriteLocker locker(&m_lock);
+ m_flushing = true;
+ return S_OK;
+ }
+
+ STDMETHODIMP QMemInputPin::EndFlush()
+ {
+ //pass downstream
+ for(int i = 0; i < m_outputs.count(); ++i) {
+ IPin *conn = m_outputs.at(i)->connected();
+ if (conn) {
+ conn->EndFlush();
+ }
+ }
+ QWriteLocker locker(&m_lock);
+ m_flushing = false;
+ return S_OK;
+ }
+
+ STDMETHODIMP QMemInputPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
+ {
+ for(int i = 0; i < m_outputs.count(); ++i) {
+ m_outputs.at(i)->NewSegment(start, stop, rate);
+ }
+ return S_OK;
+ }
+
+ //reimplementation to set the type for the output pin
+ //no need to make a deep copy here
+ STDMETHODIMP QMemInputPin::ReceiveConnection(IPin *pin ,const AM_MEDIA_TYPE *mt)
+ {
+ HRESULT hr = QPin::ReceiveConnection(pin, mt);
+ if (hr == S_OK &&
+ mt->majortype != MEDIATYPE_NULL &&
+ mt->subtype != MEDIASUBTYPE_NULL &&
+ mt->formattype != GUID_NULL) {
+ //we tell the output pins that they should connect with this type
+ for(int i = 0; i < m_outputs.count(); ++i) {
+ hr = m_outputs.at(i)->setAcceptedMediaType(connectedType());
+ if (FAILED(hr)) {
+ break;
+ }
+ }
+ }
+ return hr;
+ }
+
+ STDMETHODIMP QMemInputPin::GetAllocator(IMemAllocator **alloc)
+ {
+ if (!alloc) {
+ return E_POINTER;
+ }
+
+ if (*alloc = memoryAllocator(true)) {
+ return S_OK;
+ }
+
+ return VFW_E_NO_ALLOCATOR;
+ }
+
+ STDMETHODIMP QMemInputPin::NotifyAllocator(IMemAllocator *alloc, BOOL readonly)
+ {
+ if (!alloc) {
+ return E_POINTER;
+ }
+
+ {
+ QWriteLocker locker(&m_lock);
+ m_shouldDuplicateSamples = m_transform && readonly;
+ }
+
+ setMemoryAllocator(alloc);
+
+ for(int i = 0; i < m_outputs.count(); ++i) {
+ IPin *pin = m_outputs.at(i)->connected();
+ if (pin) {
+ ComPointer<IMemInputPin> input(pin, IID_IMemInputPin);
+ input->NotifyAllocator(alloc, m_shouldDuplicateSamples);
+ }
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP QMemInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *prop)
+ {
+ if (!prop) {
+ return E_POINTER;
+ }
+
+ //we have no particular requirements
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP QMemInputPin::Receive(IMediaSample *sample)
+ {
+ QMutexLocker locker(&m_mutexReceive);
+ if (!sample) {
+ return E_POINTER;
+ }
+
+ if (filterState() == State_Stopped) {
+ return VFW_E_WRONG_STATE;
+ }
+
+ if (isFlushing()) {
+ return S_FALSE; //we are still flushing
+ }
+
+ if (!m_shouldDuplicateSamples) {
+ //we do it just once
+ HRESULT hr = m_parent->processSample(sample);
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ }
+
+ for (int i = 0; i < m_outputs.count(); ++i) {
+ QPin *current = m_outputs.at(i);
+ IMediaSample *outSample = m_shouldDuplicateSamples ?
+ duplicateSampleForOutput(sample, current->memoryAllocator())
+ : sample;
+
+ if (m_shouldDuplicateSamples) {
+ m_parent->processSample(outSample);
+ }
+
+ IPin *pin = current->connected();
+ if (pin) {
+ ComPointer<IMemInputPin> input(pin, IID_IMemInputPin);
+ if (input) {
+ input->Receive(outSample);
+ }
+ }
+
+ if (m_shouldDuplicateSamples) {
+ outSample->Release();
+ }
+ }
+ return S_OK;
+ }
+
+ STDMETHODIMP QMemInputPin::ReceiveMultiple(IMediaSample **samples,long count,long *nbDone)
+ {
+ //no need to lock here: there is no access to member data
+ if (!samples || !nbDone) {
+ return E_POINTER;
+ }
+
+ *nbDone = 0; //initialization
+ while( *nbDone != count) {
+ HRESULT hr = Receive(samples[*nbDone]);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ (*nbDone)++;
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP QMemInputPin::ReceiveCanBlock()
+ {
+ //we test the output to see if they can block
+ for(int i = 0; i < m_outputs.count(); ++i) {
+ IPin *input = m_outputs.at(i)->connected();
+ if (input) {
+ ComPointer<IMemInputPin> meminput(input, IID_IMemInputPin);
+ if (meminput && meminput->ReceiveCanBlock() != S_FALSE) {
+ return S_OK;
+ }
+ }
+ }
+ return S_FALSE;
+ }
+
+ //addition
+ //this should be used by the filter to tell it's input pins to which output they should route the samples
+
+ void QMemInputPin::addOutput(QPin *output)
+ {
+ QWriteLocker locker(&m_lock);
+ m_outputs += output;
+ }
+
+ void QMemInputPin::removeOutput(QPin *output)
+ {
+ QWriteLocker locker(&m_lock);
+ m_outputs.removeOne(output);
+ }
+
+ QList<QPin*> QMemInputPin::outputs() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_outputs;
+ }
+
+ ALLOCATOR_PROPERTIES QMemInputPin::getDefaultAllocatorProperties() const
+ {
+ //those values reduce buffering a lot (good for the volume effect)
+ ALLOCATOR_PROPERTIES prop = {4096, 1, 1, 0};
+ return prop;
+ }
+
+
+ IMediaSample *QMemInputPin::duplicateSampleForOutput(IMediaSample *sample, IMemAllocator *alloc)
+ {
+ LONG length = sample->GetActualDataLength();
+
+ HRESULT hr = alloc->Commit();
+ if (hr == VFW_E_SIZENOTSET) {
+ ALLOCATOR_PROPERTIES prop = getDefaultAllocatorProperties();
+ prop.cbBuffer = qMax(prop.cbBuffer, length);
+ ALLOCATOR_PROPERTIES actual;
+ //we just try to set the properties...
+ alloc->SetProperties(&prop, &actual);
+ hr = alloc->Commit();
+ }
+
+ Q_ASSERT(SUCCEEDED(hr));
+
+ IMediaSample *out;
+ hr = alloc->GetBuffer(&out, 0, 0, AM_GBF_NOTASYNCPOINT);
+ Q_ASSERT(SUCCEEDED(hr));
+
+ //let's copy the sample
+ {
+ REFERENCE_TIME start, end;
+ sample->GetTime(&start, &end);
+ out->SetTime(&start, &end);
+ }
+
+ hr = out->SetActualDataLength(length);
+ Q_ASSERT(SUCCEEDED(hr));
+ hr = out->SetDiscontinuity(sample->IsDiscontinuity());
+ Q_ASSERT(SUCCEEDED(hr));
+
+ {
+ LONGLONG start, end;
+ hr = sample->GetMediaTime(&start, &end);
+ if (hr != VFW_E_MEDIA_TIME_NOT_SET) {
+ hr = out->SetMediaTime(&start, &end);
+ Q_ASSERT(SUCCEEDED(hr));
+ }
+ }
+
+ AM_MEDIA_TYPE *type = 0;
+ hr = sample->GetMediaType(&type);
+ Q_ASSERT(SUCCEEDED(hr));
+ hr = out->SetMediaType(type);
+ Q_ASSERT(SUCCEEDED(hr));
+
+ hr = out->SetPreroll(sample->IsPreroll());
+ Q_ASSERT(SUCCEEDED(hr));
+ hr = out->SetSyncPoint(sample->IsSyncPoint());
+ Q_ASSERT(SUCCEEDED(hr));
+
+ BYTE *dest = 0, *src = 0;
+ hr = out->GetPointer(&dest);
+ Q_ASSERT(SUCCEEDED(hr));
+ hr = sample->GetPointer(&src);
+ Q_ASSERT(SUCCEEDED(hr));
+
+ qMemCopy(dest, src, sample->GetActualDataLength());
+
+ return out;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/qmeminputpin.h b/src/3rdparty/phonon/ds9/qmeminputpin.h
new file mode 100644
index 0000000..c449721
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qmeminputpin.h
@@ -0,0 +1,82 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_QMEMINPUTPIN_H
+#define PHONON_QMEMINPUTPIN_H
+
+
+#include <QtCore/QList>
+#include <QtCore/QMutex>
+#include "qpin.h"
+
+#include <dshow.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class QBaseFilter;
+
+ //this class will be used for our effects
+ class QMemInputPin : public QPin, public IMemInputPin
+ {
+ public:
+ QMemInputPin(QBaseFilter *, const QVector<AM_MEDIA_TYPE> &, bool transform);
+ ~QMemInputPin();
+
+ //reimplementation from IUnknown
+ STDMETHODIMP QueryInterface(REFIID iid, void** out);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ //reimplementation from IPin
+ STDMETHODIMP ReceiveConnection(IPin *,const AM_MEDIA_TYPE *);
+ STDMETHODIMP BeginFlush();
+ STDMETHODIMP EndFlush();
+ STDMETHODIMP EndOfStream();
+ STDMETHODIMP NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate);
+
+ //reimplementation from IMemAllocator
+ STDMETHODIMP GetAllocator(IMemAllocator **);
+ STDMETHODIMP NotifyAllocator(IMemAllocator *,BOOL);
+ STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *);
+ STDMETHODIMP Receive(IMediaSample *);
+ STDMETHODIMP ReceiveMultiple(IMediaSample **,long,long *);
+ STDMETHODIMP ReceiveCanBlock();
+
+ //addition
+ void addOutput(QPin *output);
+ void removeOutput(QPin *output);
+ QList<QPin*> outputs() const;
+
+ private:
+ IMediaSample *duplicateSampleForOutput(IMediaSample *, IMemAllocator *);
+ ALLOCATOR_PROPERTIES getDefaultAllocatorProperties() const;
+
+ bool m_shouldDuplicateSamples;
+ const bool m_transform; //defines if the pin is transforming the samples
+ QList<QPin*> m_outputs;
+ QMutex m_mutexReceive;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/3rdparty/phonon/ds9/qpin.cpp b/src/3rdparty/phonon/ds9/qpin.cpp
new file mode 100644
index 0000000..37fe48d
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qpin.cpp
@@ -0,0 +1,653 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "qbasefilter.h"
+#include "qpin.h"
+#include "compointer.h"
+
+#include <QtCore/QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+
+ static const AM_MEDIA_TYPE defaultMediaType()
+ {
+ AM_MEDIA_TYPE ret;
+ ret.majortype = MEDIATYPE_NULL;
+ ret.subtype = MEDIASUBTYPE_NULL;
+ ret.bFixedSizeSamples = TRUE;
+ ret.bTemporalCompression = FALSE;
+ ret.lSampleSize = 1;
+ ret.formattype = GUID_NULL;
+ ret.pUnk = 0;
+ ret.cbFormat = 0;
+ ret.pbFormat = 0;
+ return ret;
+ }
+
+ class QEnumMediaTypes : public IEnumMediaTypes
+ {
+ public:
+ QEnumMediaTypes(QPin *pin) : m_refCount(1), m_pin(pin), m_index(0)
+ {
+ m_pin->AddRef();
+ }
+
+ ~QEnumMediaTypes()
+ {
+ m_pin->Release();
+ }
+
+ STDMETHODIMP QueryInterface(const IID &iid,void **out)
+ {
+ if (!out) {
+ return E_POINTER;
+ }
+
+ HRESULT hr = S_OK;
+ if (iid == IID_IEnumMediaTypes) {
+ *out = static_cast<IEnumMediaTypes*>(this);
+ } else if (iid == IID_IUnknown) {
+ *out = static_cast<IUnknown*>(this);
+ } else {
+ *out = 0;
+ hr = E_NOINTERFACE;
+ }
+
+ if (hr == S_OK) {
+ AddRef();
+ }
+ return hr;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef()
+ {
+ return InterlockedIncrement(&m_refCount);
+ }
+
+ STDMETHODIMP_(ULONG) Release()
+ {
+ ULONG refCount = InterlockedDecrement(&m_refCount);
+ if (refCount == 0) {
+ delete this;
+ }
+
+ return refCount;
+ }
+
+ STDMETHODIMP Next(ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!out) {
+ return E_POINTER;
+ }
+
+ if (!fetched && count > 1) {
+ return E_INVALIDARG;
+ }
+
+ int nbFetched = 0;
+ while (nbFetched < int(count) && m_index < m_pin->mediaTypes().count()) {
+ //the caller will deallocate the memory
+ *out = static_cast<AM_MEDIA_TYPE *>(::CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)));
+ const AM_MEDIA_TYPE original = m_pin->mediaTypes().at(m_index);
+ **out = QPin::copyMediaType(original);
+ nbFetched++;
+ m_index++;
+ out++;
+ }
+
+ if (fetched) {
+ *fetched = nbFetched;
+ }
+
+ return nbFetched == count ? S_OK : S_FALSE;
+ }
+
+ STDMETHODIMP Skip(ULONG count)
+ {
+ QMutexLocker locker(&m_mutex);
+ m_index = qMin(m_index + int(count), m_pin->mediaTypes().count());
+ return (m_index == m_pin->mediaTypes().count()) ? S_FALSE : S_OK;
+ }
+
+ STDMETHODIMP Reset()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_index = 0;
+ return S_OK;
+ }
+
+ STDMETHODIMP Clone(IEnumMediaTypes **out)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!out) {
+ return E_POINTER;
+ }
+
+ *out = new QEnumMediaTypes(m_pin);
+ (*out)->Skip(m_index);
+ return S_OK;
+ }
+
+
+ private:
+ LONG m_refCount;
+ QPin *m_pin;
+ int m_index;
+ QMutex m_mutex;
+ };
+
+
+ QPin::QPin(QBaseFilter *parent, PIN_DIRECTION dir, const QVector<AM_MEDIA_TYPE> &mt) :
+ m_memAlloc(0), m_parent(parent), m_refCount(1), m_connected(0),
+ m_direction(dir), m_mediaTypes(mt), m_connectedType(defaultMediaType()),
+ m_flushing(false)
+ {
+ Q_ASSERT(m_parent);
+ m_parent->addPin(this);
+ }
+
+ QPin::~QPin()
+ {
+ m_parent->removePin(this);
+ setMemoryAllocator(0);
+ freeMediaType(m_connectedType);
+ }
+
+ //reimplementation from IUnknown
+ STDMETHODIMP QPin::QueryInterface(REFIID iid, void**out)
+ {
+ if (!out) {
+ return E_POINTER;
+ }
+
+ HRESULT hr = S_OK;
+
+ if (iid == IID_IPin) {
+ *out = static_cast<IPin*>(this);
+ } else if (iid == IID_IUnknown) {
+ *out = static_cast<IUnknown*>(this);
+ } else if (m_direction == PINDIR_OUTPUT && (iid == IID_IMediaSeeking || iid == IID_IMediaPosition)) {
+ return m_parent->QueryInterface(iid, out);
+ } else {
+ *out = 0;
+ hr = E_NOINTERFACE;
+ }
+
+ if (hr == S_OK) {
+ AddRef();
+ }
+ return hr;
+ }
+
+ STDMETHODIMP_(ULONG) QPin::AddRef()
+ {
+ return InterlockedIncrement(&m_refCount);
+ }
+
+ STDMETHODIMP_(ULONG) QPin::Release()
+ {
+ ULONG refCount = InterlockedDecrement(&m_refCount);
+ if (refCount == 0) {
+ delete this;
+ }
+
+ return refCount;
+ }
+
+ //this is called on the input pins
+ STDMETHODIMP QPin::ReceiveConnection(IPin *pin, const AM_MEDIA_TYPE *type)
+ {
+ if (!pin ||!type) {
+ return E_POINTER;
+ }
+
+ if (connected()) {
+ return VFW_E_ALREADY_CONNECTED;
+ }
+
+ if (filterState() != State_Stopped) {
+ return VFW_E_NOT_STOPPED;
+ }
+
+ if (QueryAccept(type) != S_OK) {
+ return VFW_E_TYPE_NOT_ACCEPTED;
+ }
+
+ setConnected(pin);
+ setConnectedType(*type);
+
+ return S_OK;
+ }
+
+ //this is called on the output pins
+ STDMETHODIMP QPin::Connect(IPin *pin, const AM_MEDIA_TYPE *type)
+ {
+ if (!pin) {
+ return E_POINTER;
+ }
+
+ if (connected()) {
+ return VFW_E_ALREADY_CONNECTED;
+ }
+
+ if (filterState() != State_Stopped) {
+ return VFW_E_NOT_STOPPED;
+ }
+
+ HRESULT hr = S_OK;
+
+ setConnected(pin);
+ if (!type) {
+
+ //let(s first try the output pin's mediaTypes
+ if (checkOutputMediaTypesConnection(pin) != S_OK &&
+ checkOwnMediaTypesConnection(pin) != S_OK) {
+ hr = VFW_E_NO_ACCEPTABLE_TYPES;
+ }
+ } else if (QueryAccept(type) == S_OK) {
+ setConnectedType(*type);
+ hr = pin->ReceiveConnection(this, type);
+ } else {
+ hr = VFW_E_TYPE_NOT_ACCEPTED;
+ }
+
+ if (FAILED(hr)) {
+ setConnected(0);
+ setConnectedType(defaultMediaType());
+ } else {
+ ComPointer<IMemInputPin> input(pin, IID_IMemInputPin);
+ if (input) {
+ ComPointer<IMemAllocator> alloc;
+ input->GetAllocator(alloc.pparam());
+ if (alloc) {
+ //be default we take the allocator from the input pin
+ //we have no reason to force using our own
+ setMemoryAllocator(alloc);
+ }
+ }
+ if (memoryAllocator() == 0) {
+ ALLOCATOR_PROPERTIES prop;
+ if (input && input->GetAllocatorRequirements(&prop) == S_OK) {
+ createDefaultMemoryAllocator(&prop);
+ } else {
+ createDefaultMemoryAllocator();
+ }
+ }
+
+ Q_ASSERT(memoryAllocator() != 0);
+ if (input) {
+ input->NotifyAllocator(memoryAllocator(), TRUE); //TRUE is arbitrarily chosen here
+ }
+
+ }
+
+ return hr;
+ }
+
+ STDMETHODIMP QPin::Disconnect()
+ {
+ if (!connected()) {
+ return S_FALSE;
+ }
+
+ if (filterState() != State_Stopped) {
+ return VFW_E_NOT_STOPPED;
+ }
+
+ setConnected(0);
+ setConnectedType(defaultMediaType());
+ if (m_direction == PINDIR_INPUT) {
+ setMemoryAllocator(0);
+ }
+ return S_OK;
+ }
+
+ STDMETHODIMP QPin::ConnectedTo(IPin **other)
+ {
+ if (!other) {
+ return E_POINTER;
+ }
+
+ *other = connected(true);
+ if (!(*other)) {
+ return VFW_E_NOT_CONNECTED;
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP QPin::ConnectionMediaType(AM_MEDIA_TYPE *type)
+ {
+ QReadLocker locker(&m_lock);
+ if (!type) {
+ return E_POINTER;
+ }
+
+ *type = copyMediaType(m_connectedType);
+ if (!m_connected) {
+ return VFW_E_NOT_CONNECTED;
+ } else {
+ return S_OK;
+ }
+ }
+
+ STDMETHODIMP QPin::QueryPinInfo(PIN_INFO *info)
+ {
+ QReadLocker locker(&m_lock);
+ if (!info) {
+ return E_POINTER;
+ }
+
+ info->dir = m_direction;
+ info->pFilter = m_parent;
+ m_parent->AddRef();
+ qMemCopy(info->achName, m_name.utf16(), qMin(MAX_FILTER_NAME, m_name.length()+1) *2);
+
+ return S_OK;
+ }
+
+ STDMETHODIMP QPin::QueryDirection(PIN_DIRECTION *dir)
+ {
+ QReadLocker locker(&m_lock);
+ if (!dir) {
+ return E_POINTER;
+ }
+
+ *dir = m_direction;
+ return S_OK;
+ }
+
+ STDMETHODIMP QPin::QueryId(LPWSTR *id)
+ {
+ QReadLocker locker(&m_lock);
+ if (!id) {
+ return E_POINTER;
+ }
+
+ int nbBytes = (m_name.length()+1)*2;
+ *id = static_cast<LPWSTR>(::CoTaskMemAlloc(nbBytes));
+ qMemCopy(*id, m_name.utf16(), nbBytes);
+ return S_OK;
+ }
+
+ STDMETHODIMP QPin::QueryAccept(const AM_MEDIA_TYPE *type)
+ {
+ QReadLocker locker(&m_lock);
+ if (!type) {
+ return E_POINTER;
+ }
+
+ for (int i = 0; i < m_mediaTypes.count(); ++i) {
+ const AM_MEDIA_TYPE &current = m_mediaTypes.at(i);
+ if ( (type->majortype == current.majortype) &&
+ (current.subtype == MEDIASUBTYPE_NULL || type->subtype == current.subtype) &&
+ (type->majortype == MEDIATYPE_Stream || type->formattype != GUID_NULL || current.formattype != GUID_NULL) &&
+ (current.formattype == GUID_NULL || type->formattype == current.formattype)
+ ) {
+ return S_OK;
+ }
+ }
+ return S_FALSE;
+ }
+
+
+ STDMETHODIMP QPin::EnumMediaTypes(IEnumMediaTypes **emt)
+ {
+ if (!emt) {
+ return E_POINTER;
+ }
+
+ *emt = new QEnumMediaTypes(this);
+ return S_OK;
+ }
+
+
+ STDMETHODIMP QPin::EndOfStream()
+ {
+ return E_UNEXPECTED;
+ }
+
+ STDMETHODIMP QPin::BeginFlush()
+ {
+ return E_UNEXPECTED;
+ }
+
+ STDMETHODIMP QPin::EndFlush()
+ {
+ return E_UNEXPECTED;
+ }
+
+ STDMETHODIMP QPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
+ {
+ QReadLocker locker(&m_lock);
+ if (m_direction == PINDIR_OUTPUT && m_connected) {
+ //we deliver this downstream
+ m_connected->NewSegment(start, stop, rate);
+ }
+ return S_OK;
+ }
+
+ STDMETHODIMP QPin::QueryInternalConnections(IPin **, ULONG*)
+ {
+ //this is not implemented on purpose (the input pins are connected to all the output pins)
+ return E_NOTIMPL;
+ }
+
+
+ HRESULT QPin::checkOutputMediaTypesConnection(IPin *pin)
+ {
+ IEnumMediaTypes *emt = 0;
+ HRESULT hr = pin->EnumMediaTypes(&emt);
+ if (hr != S_OK) {
+ return hr;
+ }
+
+ AM_MEDIA_TYPE *type = 0;
+ while (emt->Next(1, &type, 0) == S_OK) {
+ if (QueryAccept(type) == S_OK) {
+ setConnectedType(*type);
+ if (pin->ReceiveConnection(this, type) == S_OK) {
+ freeMediaType(type);
+ return S_OK;
+ } else {
+ setConnectedType(defaultMediaType());
+ freeMediaType(type);
+ }
+ }
+ }
+
+ //we didn't find a suitable type
+ return S_FALSE;
+ }
+
+ HRESULT QPin::checkOwnMediaTypesConnection(IPin *pin)
+ {
+ for(int i = 0; i < m_mediaTypes.count(); ++i) {
+ const AM_MEDIA_TYPE &current = m_mediaTypes.at(i);
+ setConnectedType(current);
+ HRESULT hr = pin->ReceiveConnection(this, &current);
+ if (hr == S_OK) {
+ return S_OK;
+ }
+ }
+
+ //we didn't find a suitable type
+ return S_FALSE;
+ }
+
+ void QPin::freeMediaType(const AM_MEDIA_TYPE &type)
+ {
+ if (type.cbFormat) {
+ ::CoTaskMemFree(type.pbFormat);
+ }
+ if (type.pUnk) {
+ type.pUnk->Release();
+ }
+ }
+
+ void QPin::freeMediaType(AM_MEDIA_TYPE *type)
+ {
+ freeMediaType(*type);
+ ::CoTaskMemFree(type);
+ }
+
+ //addition
+
+ PIN_DIRECTION QPin::direction() const
+ {
+ return m_direction;
+ }
+
+ void QPin::setConnectedType(const AM_MEDIA_TYPE &type)
+ {
+ QWriteLocker locker(&m_lock);
+
+ //1st we free memory
+ freeMediaType(m_connectedType);
+
+ m_connectedType = copyMediaType(type);
+ }
+
+ const AM_MEDIA_TYPE &QPin::connectedType() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_connectedType;
+ }
+
+ void QPin::setConnected(IPin *pin)
+ {
+ QWriteLocker locker(&m_lock);
+ if (pin) {
+ pin->AddRef();
+ }
+ if (m_connected) {
+ m_connected->Release();
+ }
+ m_connected = pin;
+ }
+
+ IPin *QPin::connected(bool addref) const
+ {
+ QReadLocker locker(&m_lock);
+ if (addref && m_connected) {
+ m_connected->AddRef();
+ }
+ return m_connected;
+ }
+
+ bool QPin::isFlushing() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_flushing;
+ }
+
+ FILTER_STATE QPin::filterState() const
+ {
+ QReadLocker locker(&m_lock);
+ FILTER_STATE fstate = State_Stopped;
+ m_parent->GetState(0, &fstate);
+ return fstate;
+ }
+
+ QVector<AM_MEDIA_TYPE> QPin::mediaTypes() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_mediaTypes;
+ }
+
+ HRESULT QPin::setAcceptedMediaType(const AM_MEDIA_TYPE &mt)
+ {
+ const QVector<AM_MEDIA_TYPE> oldMediaTypes = m_mediaTypes;
+ m_mediaTypes = QVector<AM_MEDIA_TYPE>() << mt;
+
+ HRESULT hr = S_OK;
+
+ IPin *conn = connected();
+ if (conn) {
+ //try to reconnect to redefine the media type
+ conn->Disconnect();
+ Disconnect();
+ hr = Connect(conn, 0);
+ if (FAILED(hr)) {
+ m_mediaTypes = oldMediaTypes;
+ Connect(conn, 0); //just redo the connection with the old media types
+ }
+ }
+ return hr;
+ }
+
+ void QPin::createDefaultMemoryAllocator(ALLOCATOR_PROPERTIES *prop)
+ {
+ ComPointer<IMemAllocator> alloc(CLSID_MemoryAllocator, IID_IMemAllocator);
+ if (prop) {
+ alloc->SetProperties(prop, 0);
+ }
+ setMemoryAllocator(alloc);
+ }
+
+ void QPin::setMemoryAllocator(IMemAllocator *alloc)
+ {
+ QWriteLocker locker(&m_lock);
+ if (alloc) {
+ alloc->AddRef();
+ }
+ if (m_memAlloc) {
+ m_memAlloc->Release();
+ }
+ m_memAlloc = alloc;
+ }
+
+ IMemAllocator *QPin::memoryAllocator(bool addref) const
+ {
+ QReadLocker locker(&m_lock);
+ if (addref && m_memAlloc) {
+ m_memAlloc->AddRef();
+ }
+ return m_memAlloc;
+ }
+
+ AM_MEDIA_TYPE QPin::copyMediaType(const AM_MEDIA_TYPE &type)
+ {
+ AM_MEDIA_TYPE ret = type;
+
+ //make a deep copy here
+ if (ret.cbFormat == 0 || ret.pbFormat == 0) {
+ ret.cbFormat = 0;
+ ret.pbFormat = 0;
+ ret.formattype = GUID_NULL;
+ } else {
+ ret.pbFormat = reinterpret_cast<BYTE*>(::CoTaskMemAlloc(type.cbFormat));
+ qMemCopy(ret.pbFormat, type.pbFormat, type.cbFormat);
+ }
+
+ if (type.pUnk) {
+ type.pUnk->AddRef();
+ }
+ return ret;
+ }
+
+
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/3rdparty/phonon/ds9/qpin.h b/src/3rdparty/phonon/ds9/qpin.h
new file mode 100644
index 0000000..a3287c4
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/qpin.h
@@ -0,0 +1,121 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_QPIN_H
+#define PHONON_QPIN_H
+
+#include "phononds9_namespace.h"
+
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <QtCore/QReadWriteLock>
+
+#include <dshow.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class QBaseFilter;
+
+ //this is the base class for our self-implemented Pins
+ class QPin : public IPin
+ {
+ public:
+ QPin(QBaseFilter *parent, PIN_DIRECTION dir, const QVector<AM_MEDIA_TYPE> &mt);
+ virtual ~QPin();
+
+ //reimplementation from IUnknown
+ STDMETHODIMP QueryInterface(REFIID iid, void** out);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ //reimplementation from IPin
+ STDMETHODIMP Connect(IPin *,const AM_MEDIA_TYPE *);
+ STDMETHODIMP ReceiveConnection(IPin *,const AM_MEDIA_TYPE *);
+ STDMETHODIMP Disconnect();
+ STDMETHODIMP ConnectedTo(IPin **);
+ STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *);
+ STDMETHODIMP QueryPinInfo(PIN_INFO *);
+ STDMETHODIMP QueryDirection(PIN_DIRECTION *);
+ STDMETHODIMP QueryId(LPWSTR*);
+ STDMETHODIMP QueryAccept(const AM_MEDIA_TYPE*);
+ STDMETHODIMP EnumMediaTypes(IEnumMediaTypes **);
+ STDMETHODIMP QueryInternalConnections(IPin **, ULONG*);
+ STDMETHODIMP EndOfStream();
+ STDMETHODIMP BeginFlush();
+ STDMETHODIMP EndFlush();
+ STDMETHODIMP NewSegment(REFERENCE_TIME, REFERENCE_TIME, double);
+
+ QVector<AM_MEDIA_TYPE> mediaTypes() const;
+
+ HRESULT setAcceptedMediaType(const AM_MEDIA_TYPE &);
+
+ bool isFlushing() const;
+ void setConnectedType(const AM_MEDIA_TYPE &type);
+ const AM_MEDIA_TYPE &connectedType() const;
+ void setConnected(IPin *pin);
+ IPin *connected(bool = false) const;
+ void setMemoryAllocator(IMemAllocator *alloc);
+ IMemAllocator *memoryAllocator(bool = false) const;
+ void createDefaultMemoryAllocator(ALLOCATOR_PROPERTIES * = 0);
+ PIN_DIRECTION direction() const;
+
+ FILTER_STATE filterState() const;
+
+ static AM_MEDIA_TYPE copyMediaType(const AM_MEDIA_TYPE &type);
+ static void freeMediaType(AM_MEDIA_TYPE *type);
+ static void freeMediaType(const AM_MEDIA_TYPE &type);
+
+ protected:
+ //this can be used by sub-classes
+ mutable QReadWriteLock m_lock;
+ QBaseFilter *m_parent;
+ bool m_flushing;
+
+ private:
+ HRESULT checkOutputMediaTypesConnection(IPin *pin);
+ HRESULT checkOwnMediaTypesConnection(IPin *pin);
+
+ LONG m_refCount;
+ IPin *m_connected;
+ const PIN_DIRECTION m_direction;
+ QVector<AM_MEDIA_TYPE> m_mediaTypes; //accepted media types
+ AM_MEDIA_TYPE m_connectedType;
+ QString m_name;
+ IMemAllocator *m_memAlloc;
+ };
+
+ //utility function
+ class QAMMediaType : public AM_MEDIA_TYPE
+ {
+ public:
+ ~QAMMediaType()
+ {
+ QPin::freeMediaType(*this);
+ }
+
+ };
+
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //PHONON_QPIN_H
diff --git a/src/3rdparty/phonon/ds9/videorenderer_soft.cpp b/src/3rdparty/phonon/ds9/videorenderer_soft.cpp
new file mode 100644
index 0000000..dd6e076
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/videorenderer_soft.cpp
@@ -0,0 +1,1011 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "videorenderer_soft.h"
+
+#ifndef QT_NO_PHONON_VIDEO
+
+#include "qmeminputpin.h"
+#include "qbasefilter.h"
+
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEngine>
+#include <QtGui/QApplication>
+#include <QtCore/QTime>
+
+#define _USE_MATH_DEFINES //for pi
+#include <QtCore/qmath.h> //for sin and cos
+/* M_PI is a #define that may or may not be handled in <cmath> */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846264338327950288419717
+#endif
+
+#include <dvdmedia.h> //for VIDEOINFOHEADER2
+
+//this will make a display every second of how many frames were pocessed and actually displayed
+//#define FPS_COUNTER
+
+#ifdef Q_OS_WINCE
+#define QT_NO_OPENGL
+#endif
+
+#ifndef QT_NO_OPENGL
+#include <gl/gl.h>
+#ifndef GL_FRAGMENT_PROGRAM_ARB
+#define GL_FRAGMENT_PROGRAM_ARB 0x8804
+#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
+#endif
+
+// support old OpenGL installations (1.2)
+// assume that if TEXTURE0 isn't defined, none are
+#ifndef GL_TEXTURE0
+# define GL_TEXTURE0 0x84C0
+# define GL_TEXTURE1 0x84C1
+# define GL_TEXTURE2 0x84C2
+#endif
+
+// arbfp1 fragment program for converting yuv (YV12) to rgb
+static const char yv12ToRgb[] =
+"!!ARBfp1.0"
+"PARAM c[5] = { program.local[0..1],"
+" { 1.164, 0, 1.596, 0.5 },"
+" { 0.0625, 1.164, -0.391, -0.81300002 },"
+" { 1.164, 2.0179999, 0 } };"
+"TEMP R0;"
+"TEX R0.x, fragment.texcoord[0], texture[1], 2D;"
+"ADD R0.y, R0.x, -c[2].w;"
+"TEX R0.x, fragment.texcoord[0], texture[2], 2D;"
+"ADD R0.x, R0, -c[2].w;"
+"MUL R0.z, R0.y, c[0].w;"
+"MAD R0.z, R0.x, c[0], R0;"
+"MUL R0.w, R0.x, c[0];"
+"MUL R0.z, R0, c[0].y;"
+"TEX R0.x, fragment.texcoord[0], texture[0], 2D;"
+"MAD R0.y, R0, c[0].z, R0.w;"
+"ADD R0.x, R0, -c[3];"
+"MUL R0.y, R0, c[0];"
+"MUL R0.z, R0, c[1].x;"
+"MAD R0.x, R0, c[0].y, c[0];"
+"MUL R0.y, R0, c[1].x;"
+"DP3 result.color.x, R0, c[2];"
+"DP3 result.color.y, R0, c[3].yzww;"
+"DP3 result.color.z, R0, c[4];"
+"MOV result.color.w, c[1].y;"
+"END";
+
+static const char yuy2ToRgb[] =
+ "!!ARBfp1.0"
+"PARAM c[5] = { program.local[0..1],"
+" { 0.5, 2, 1, 0.0625 },"
+" { 1.164, 0, 1.596, 2.0179999 },"
+" { 1.164, -0.391, -0.81300002 } };"
+"TEMP R0;"
+"TEMP R1;"
+"TEMP R2;"
+"FLR R1.z, fragment.texcoord[0].x;"
+"ADD R0.x, R1.z, c[2];"
+"ADD R1.z, fragment.texcoord[0].x, -R1;"
+"MUL R1.x, fragment.texcoord[0].z, R0;"
+"MOV R1.y, fragment.texcoord[0];"
+"TEX R0, R1, texture[0], 2D;"
+"ADD R1.y, R0.z, -R0.x;"
+"MUL R2.x, R1.z, R1.y;"
+"MAD R0.x, R2, c[2].y, R0;"
+"MOV R1.y, fragment.texcoord[0];"
+"ADD R1.x, fragment.texcoord[0].z, R1;"
+"TEX R1.xyw, R1, texture[0], 2D;"
+"ADD R2.x, R1, -R0.z;"
+"MAD R1.x, R1.z, c[2].y, -c[2].z;"
+"MAD R0.z, R1.x, R2.x, R0;"
+"ADD R1.xy, R1.ywzw, -R0.ywzw;"
+"ADD R0.z, R0, -R0.x;"
+"SGE R1.w, R1.z, c[2].x;"
+"MAD R0.x, R1.w, R0.z, R0;"
+"MAD R0.yz, R1.z, R1.xxyw, R0.xyww;"
+"ADD R0.xyz, R0, -c[2].wxxw;"
+"MUL R0.w, R0.y, c[0];"
+"MAD R0.w, R0.z, c[0].z, R0;"
+"MUL R0.z, R0, c[0].w;"
+"MAD R0.y, R0, c[0].z, R0.z;"
+"MUL R0.w, R0, c[0].y;"
+"MUL R0.y, R0, c[0];"
+"MUL R0.z, R0.w, c[1].x;"
+"MAD R0.x, R0, c[0].y, c[0];"
+"MUL R0.y, R0, c[1].x;"
+"DP3 result.color.x, R0, c[3];"
+"DP3 result.color.y, R0, c[4];"
+"DP3 result.color.z, R0, c[3].xwyw;"
+"MOV result.color.w, c[1].y;"
+"END";
+
+#endif //QT_NO_OPENGL
+
+#define CLIP_SHIFT_RIGHT_8(c) ((c) < 0 ? 0 : (c) > 0xffff ? 0xff : (c) >> 8)
+#define CLIP_SHIFT_LEFT_8(c) ((c) < 0 ? 0 : (c) > 0xffff ? 0xff0000 : ( ((c) << 8) & 0xff0000) )
+#define CLIP_NO_SHIFT(c) ((c) < 0 ? 0 : (c) > 0xffff ? 0xff00 : ((c) & 0xff00) )
+#define CLIPPED_PIXEL(base, r, g, b) (0xff000000u | CLIP_SHIFT_LEFT_8(base+r) | CLIP_NO_SHIFT(base+g) | CLIP_SHIFT_RIGHT_8(base+b))
+#define CLIPPED_PIXEL2(r, g, b) (0xff000000u | CLIP_SHIFT_LEFT_8(r) | CLIP_NO_SHIFT(g) | CLIP_SHIFT_RIGHT_8(b))
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ static const QVector<AM_MEDIA_TYPE> videoMediaTypes()
+ {
+ AM_MEDIA_TYPE mt;
+ qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE));
+ mt.majortype = MEDIATYPE_Video;
+
+ //we accept any video format
+ mt.formattype = GUID_NULL;
+ mt.cbFormat = 0;
+ mt.pbFormat = 0;
+
+ QVector<AM_MEDIA_TYPE> ret;
+
+ //we support YUV (YV12 and YUY2) and RGB32
+ mt.subtype = MEDIASUBTYPE_YV12;
+ ret << mt;
+ mt.subtype = MEDIASUBTYPE_YUY2;
+ ret << mt;
+ mt.subtype = MEDIASUBTYPE_RGB32;
+ ret << mt;
+
+ return ret;
+ }
+
+ class VideoRendererSoftFilter : public QBaseFilter
+ {
+ public:
+ VideoRendererSoftFilter(VideoRendererSoft *renderer);
+
+ ~VideoRendererSoftFilter();
+
+ QSize videoSize() const;
+
+#ifndef QT_NO_OPENGL
+ void freeGLResources()
+ {
+ if (m_usingOpenGL) {
+ //let's reinitialize those values
+ m_usingOpenGL = false;
+ //to be sure we recreate it
+ if (m_textureUploaded) {
+ glDeleteTextures(3, m_texture);
+ m_textureUploaded = false;
+ }
+ }
+ m_checkedPrograms = false;
+ }
+#endif // QT_NO_OPENGL
+
+ void freeResources()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_sampleBuffer = ComPointer<IMediaSample>();
+#ifndef QT_NO_OPENGL
+ freeGLResources();
+#endif // QT_NO_OPENGL
+ m_textureUploaded = false;
+ }
+
+ void endOfStream()
+ {
+ //received from the input pin
+ ::SetEvent(m_receiveCanWait); //unblocks the flow
+
+ //we send the message to the graph
+ ComPointer<IMediaEventSink> sink(graph(), IID_IMediaEventSink);
+ if (sink) {
+ sink->Notify(EC_COMPLETE, S_OK,
+ reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter*>(this)));
+ }
+ }
+
+ void freeMediaSample()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_sampleBuffer = ComPointer<IMediaSample>();
+ }
+
+ void beginFlush()
+ {
+ freeMediaSample();
+ ::SetEvent(m_receiveCanWait); //unblocks the flow
+ }
+
+ void endFlush()
+ {
+ if (m_inputPin->connected() == 0) {
+ ::SetEvent(m_receiveCanWait); //unblock the flow in receive
+ } else {
+ ::ResetEvent(m_receiveCanWait); //block the flow again
+ }
+ }
+
+ STDMETHODIMP Stop()
+ {
+ HRESULT hr = QBaseFilter::Stop();
+ beginFlush();
+ return hr;
+ }
+
+ STDMETHODIMP Pause()
+ {
+ HRESULT hr = QBaseFilter::Pause();
+ if (m_inputPin->connected() == 0) {
+ ::SetEvent(m_receiveCanWait); //unblock the flow in receive
+ } else {
+ ::ResetEvent(m_receiveCanWait); //this will block
+ }
+ return hr;
+ }
+
+ STDMETHODIMP Run(REFERENCE_TIME start)
+ {
+ HRESULT hr = QBaseFilter::Run(start);
+ m_start = start;
+
+ if (m_inputPin->connected() == 0) {
+ endOfStream();
+ } else {
+ ::SetEvent(m_receiveCanWait); //unblocks the flow (this event will block then again)
+ }
+
+#ifdef FPS_COUNTER
+ fpsTime.restart();
+ nbFramesProcessed = 0;
+ nbFramesDisplayed = 0;
+#endif
+
+ return hr;
+ }
+
+ HRESULT processSample(IMediaSample *sample);
+
+ void applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation)
+ {
+ //let's normalize the values
+ m_brightness = brightness * 128;
+ m_contrast = contrast + 1.;
+ m_hue = hue * M_PI;
+ m_saturation = saturation + 1.;
+ }
+
+ QImage currentImage() const
+ {
+ return m_currentImage;
+ }
+
+ void setCurrentImage(const QImage &image)
+ {
+ QMutexLocker locker(&m_mutex);
+ m_currentImage = image;
+ }
+
+ //the following function is called from the GUI thread
+ void repaintCurrentFrame(QPainter &painter, const QRect &r);
+
+
+ protected:
+ static void convertYV12toRGB(const uchar *data, const QSize &s, QImage &dest,
+ qreal brightness, qreal contrast, qreal hue, qreal saturation);
+ static void convertYUY2toRGB(const uchar *data, const QSize &s, QImage &dest,
+ qreal brightness, qreal contrast, qreal hue, qreal saturation);
+ static void normalizeRGB(const uchar *data, const QSize &s, QImage &destImage);
+
+ private:
+ QPin *const m_inputPin;
+ ComPointer<IMediaSample> m_sampleBuffer;
+ QImage m_currentImage;
+
+
+ VideoRendererSoft *m_renderer;
+ mutable QMutex m_mutex;
+ REFERENCE_TIME m_start;
+ HANDLE m_renderEvent, m_receiveCanWait; // Signals sample to render
+ QSize m_size;
+ bool m_textureUploaded;
+
+ //mixer settings
+ qreal m_brightness,
+ m_contrast,
+ m_hue,
+ m_saturation;
+
+#ifdef FPS_COUNTER
+ QTime fpsTime;
+ int nbFramesProcessed;
+ int nbFramesDisplayed;
+#endif
+
+#ifndef QT_NO_OPENGL
+ enum Program
+ {
+ YV12toRGB = 0,
+ YUY2toRGB = 1,
+ ProgramCount = 2
+ };
+
+ void updateTexture();
+ bool checkGLPrograms();
+
+ // ARB_fragment_program
+ typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
+ typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint);
+ typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *);
+ typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *);
+ typedef void (APIENTRY *_glProgramLocalParameter4fARB) (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+ typedef void (APIENTRY *_glActiveTexture) (GLenum);
+
+ _glProgramStringARB glProgramStringARB;
+ _glBindProgramARB glBindProgramARB;
+ _glDeleteProgramsARB glDeleteProgramsARB;
+ _glGenProgramsARB glGenProgramsARB;
+ _glProgramLocalParameter4fARB glProgramLocalParameter4fARB;
+ _glActiveTexture glActiveTexture;
+
+ bool m_checkedPrograms;
+ bool m_usingOpenGL;
+ GLuint m_program[2];
+ GLuint m_texture[3];
+#endif
+ };
+
+ class VideoRendererSoftPin : public QMemInputPin
+ {
+ public:
+ VideoRendererSoftPin(VideoRendererSoftFilter *parent) :
+ QMemInputPin(parent, videoMediaTypes(), false /*no transformation of the samples*/),
+ m_renderer(parent)
+ {
+ }
+
+ STDMETHODIMP EndOfStream()
+ {
+ m_renderer->endOfStream();
+ return QMemInputPin::EndOfStream();
+ }
+
+ STDMETHODIMP ReceiveCanBlock()
+ {
+ //yes, it can block
+ return S_OK;
+ }
+
+ STDMETHODIMP BeginFlush()
+ {
+ m_renderer->beginFlush();
+ return QMemInputPin::BeginFlush();
+ }
+
+ STDMETHODIMP EndFlush()
+ {
+ m_renderer->endFlush();
+ return QMemInputPin::EndFlush();
+ }
+
+
+ STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *prop)
+ {
+ if (!prop) {
+ return E_POINTER;
+ }
+
+ //we need 2 buffers
+ prop->cBuffers = 2;
+ return S_OK;
+ }
+
+
+ STDMETHODIMP NotifyAllocator(IMemAllocator *alloc, BOOL readonly)
+ {
+ if (!alloc) {
+ return E_POINTER;
+ }
+ ALLOCATOR_PROPERTIES prop;
+ HRESULT hr = alloc->GetProperties(&prop);
+ if (SUCCEEDED(hr) && prop.cBuffers == 1) {
+ //we ask to get 2 buffers so that we don't block the flow
+ //when we addref the mediasample
+ prop.cBuffers = 2;
+ ALLOCATOR_PROPERTIES dummy;
+ alloc->SetProperties(&prop, &dummy);
+ }
+
+ return QMemInputPin::NotifyAllocator(alloc, readonly);
+ }
+
+
+
+ private:
+ VideoRendererSoftFilter * const m_renderer;
+
+ };
+
+ VideoRendererSoftFilter::VideoRendererSoftFilter(VideoRendererSoft *renderer) :
+ QBaseFilter(CLSID_NULL), m_inputPin(new VideoRendererSoftPin(this)),
+ m_renderer(renderer), m_start(0)
+#ifndef QT_NO_OPENGL
+ ,m_usingOpenGL(false), m_checkedPrograms(false), m_textureUploaded(false)
+#endif
+ {
+ m_renderEvent = ::CreateEvent(0, 0, 0, 0);
+ m_receiveCanWait = ::CreateEvent(0, 0, 0, 0);
+ //simply initialize the array with default values
+ applyMixerSettings(0., 0., 0., 0.);
+#ifndef QT_NO_OPENGL
+#endif
+ }
+
+ VideoRendererSoftFilter::~VideoRendererSoftFilter()
+ {
+ ::CloseHandle(m_renderEvent);
+ ::CloseHandle(m_receiveCanWait);
+ //this frees up resources
+ freeResources();
+ }
+
+ QSize VideoRendererSoftFilter::videoSize() const
+ {
+ QSize ret;
+ const AM_MEDIA_TYPE &mt = m_inputPin->connectedType();
+ if (mt.pbFormat && mt.pbFormat) {
+ if (mt.formattype == FORMAT_VideoInfo) {
+ const VIDEOINFOHEADER *header = reinterpret_cast<VIDEOINFOHEADER*>(mt.pbFormat);
+ const int h = qAbs(header->bmiHeader.biHeight),
+ w = qAbs(header->bmiHeader.biWidth);
+ ret = QSize(w, h);
+ } else if (mt.formattype == FORMAT_VideoInfo2) {
+ const VIDEOINFOHEADER2 *header = reinterpret_cast<VIDEOINFOHEADER2*>(mt.pbFormat);
+ const int h = qAbs(header->bmiHeader.biHeight),
+ w = qAbs(header->bmiHeader.biWidth);
+ ret = QSize(w, h);
+ }
+ }
+ return ret;
+ }
+
+
+ HRESULT VideoRendererSoftFilter::processSample(IMediaSample *sample)
+ {
+#ifdef FPS_COUNTER
+ if (fpsTime.elapsed() > 1000) {
+ qDebug("FPS_COUNTER: processed=%d, displayed=%d (%d)", nbFramesProcessed, nbFramesDisplayed, fpsTime.elapsed());
+ nbFramesProcessed = 0;
+ nbFramesDisplayed = 0;
+ fpsTime.restart();
+
+ }
+#endif
+
+ AM_MEDIA_TYPE *type = 0;
+ if (sample->GetMediaType(&type) == S_OK) {
+ //let's update the media type of the input pin
+ m_inputPin->setConnectedType(*type);
+ }
+
+
+ const AM_MEDIA_TYPE &mt = m_inputPin->connectedType();
+
+ if (mt.pbFormat == 0 || mt.cbFormat == 0) {
+ return VFW_E_INVALIDMEDIATYPE;
+ }
+
+ m_size = videoSize();
+ if (!m_size.isValid()) {
+ return VFW_E_INVALIDMEDIATYPE;
+ }
+
+#ifdef FPS_COUNTER
+ nbFramesProcessed++;
+#endif
+
+ REFERENCE_TIME start = 0, stop = 0;
+ HRESULT hr = sample->GetTime(&start, &stop);
+
+ ComPointer<IReferenceClock> clock;
+ GetSyncSource(clock.pparam());
+
+ const bool playing = SUCCEEDED(hr) && state() == State_Running && clock;
+
+ if (playing) {
+ REFERENCE_TIME current;
+ clock->GetTime(&current);
+
+ DWORD_PTR advise;
+
+ //let's synchronize here
+ clock->AdviseTime(m_start, start,
+ reinterpret_cast<HEVENT>(m_renderEvent), &advise);
+
+ HANDLE handles[] = {m_receiveCanWait, m_renderEvent};
+ if (::WaitForMultipleObjects(2, handles, false, INFINITE) == WAIT_OBJECT_0) {
+ if (state() != State_Stopped && !m_inputPin->isFlushing()) {
+ ::ResetEvent(m_receiveCanWait);
+ }
+ }
+ }
+
+
+ //the let's lock the sample to be used in the GUI thread
+ {
+ QMutexLocker locker(&m_mutex);
+ sample->AddRef();
+ m_sampleBuffer = ComPointer<IMediaSample>(sample);
+ }
+
+ //image is updated: we should update the widget
+ //we should never call directly members of target due to thread-safety
+ QApplication::postEvent(m_renderer, new QEvent(QEvent::UpdateRequest));
+
+ if (!playing) {
+ //useless to test the return value of WaitForSingleObject: timeout can't happen
+ ::WaitForSingleObject(m_receiveCanWait, INFINITE);
+ if (state() != State_Stopped && !m_inputPin->isFlushing()) {
+ ::ResetEvent(m_receiveCanWait);
+ }
+ }
+
+ //everything should be ok
+ return S_OK;
+ }
+
+#ifndef QT_NO_OPENGL
+ bool VideoRendererSoftFilter::checkGLPrograms()
+ {
+ if (!m_checkedPrograms) {
+ m_checkedPrograms = true;
+
+ glProgramStringARB = (_glProgramStringARB) wglGetProcAddress("glProgramStringARB");
+ glBindProgramARB = (_glBindProgramARB) wglGetProcAddress("glBindProgramARB");
+ glDeleteProgramsARB = (_glDeleteProgramsARB) wglGetProcAddress("glDeleteProgramsARB");
+ glGenProgramsARB = (_glGenProgramsARB) wglGetProcAddress("glGenProgramsARB");
+ glProgramLocalParameter4fARB = (_glProgramLocalParameter4fARB) wglGetProcAddress("glProgramLocalParameter4fARB");
+ glActiveTexture = (_glActiveTexture) wglGetProcAddress("glActiveTexture");
+
+ //we check only once if the widget is drawn using opengl
+ if (glProgramStringARB && glBindProgramARB && glDeleteProgramsARB &&
+ glGenProgramsARB && glActiveTexture && glProgramLocalParameter4fARB) {
+ glGenProgramsARB(2, m_program);
+
+ const char *code[] = {yv12ToRgb, yuy2ToRgb};
+
+ bool error = false;
+ for(int i = 0; i < ProgramCount && !error; ++i) {
+
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program[i]);
+
+ const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(code[i]);
+ glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
+ strlen(code[i]), gl_src);
+
+ if (glGetError() != GL_NO_ERROR) {
+ error = true;
+ }
+ }
+
+ if (error) {
+ glDeleteProgramsARB(2, m_program);
+ } else {
+ //everything went fine we store the context here (we support YV12 and YUY2)
+ m_usingOpenGL = m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12
+ || m_inputPin->connectedType().subtype == MEDIASUBTYPE_YUY2;
+ //those "textures" will be used as byte streams
+ //to pass Y, U and V data to the graphics card
+ glGenTextures(3, m_texture);
+ }
+ }
+ }
+ return m_usingOpenGL;
+ }
+
+ void VideoRendererSoftFilter::updateTexture()
+ {
+ if (!m_sampleBuffer) {
+ return; //the texture is already up2date or their is no data yet
+ }
+
+ uchar *data = 0;
+ m_sampleBuffer->GetPointer(&data);
+
+ if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12) {
+ int w[3] = { m_size.width(), m_size.width()/2, m_size.width()/2 };
+ int h[3] = { m_size.height(), m_size.height()/2, m_size.height()/2 };
+ int offs[3] = { 0, m_size.width()*m_size.height(), m_size.width()*m_size.height()*5/4 };
+
+ for (int i = 0; i < 3; ++i) {
+ glBindTexture(GL_TEXTURE_2D, m_texture[i]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w[i], h[i], 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, data + offs[i]);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+ } else { //m_inputPin->connectedType().subtype == MEDIASUBTYPE_YUY2
+ //we upload 1 texture
+ glBindTexture(GL_TEXTURE_2D, m_texture[0]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_size.width() / 2, m_size.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ }
+ m_sampleBuffer = ComPointer<IMediaSample>();
+ m_textureUploaded = true;
+ }
+#endif
+
+ void VideoRendererSoftFilter::repaintCurrentFrame(QPainter &painter, const QRect &r)
+ {
+ QMutexLocker locker(&m_mutex);
+
+#ifdef FPS_COUNTER
+ nbFramesDisplayed++;
+#endif
+
+
+#ifndef QT_NO_OPENGL
+ if (painter.paintEngine() && painter.paintEngine()->type() == QPaintEngine::OpenGL && checkGLPrograms()) {
+ //for now we only support YUV (both YV12 and YUY2)
+ updateTexture();
+
+ if (!m_textureUploaded) {
+ //we simply fill the whole video with content
+ //the callee has already set the brush
+ painter.drawRect(r);
+ return;
+ }
+
+ //let's draw the texture
+
+ //Let's pass the other arguments
+ const Program prog = (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12) ? YV12toRGB : YUY2toRGB;
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program[prog]);
+ //loading the parameters
+ glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, m_brightness / 256., m_contrast, qCos(m_hue), qSin(m_hue));
+ glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, m_saturation, painter.opacity() /*alpha */, 0. /*dummy*/, 0. /*dummy*/);
+
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+
+ const float v_array[] = { r.left(), r.top(), r.right()+1, r.top(), r.right()+1, r.bottom()+1, r.left(), r.bottom()+1 };
+
+ float tx_array[12] = {0., 0., 0., 1.,
+ 0., 0., 1., 1.,
+ 0., 0., 1., 0.};
+
+ if (prog == YUY2toRGB) {
+ const float w = m_size.width() / 2,
+ iw = 1. / w;
+
+ tx_array[3] = w;
+ tx_array[6] = w;
+
+ for (int i = 0; i < 4; ++i) {
+ tx_array[3*i + 2] = iw;
+ }
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_texture[0]);
+
+ if (prog == YV12toRGB) {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_texture[2]);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_texture[1]);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+
+ glVertexPointer(2, GL_FLOAT, 0, v_array);
+ glTexCoordPointer(3, GL_FLOAT, 0, tx_array);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays(GL_QUADS, 0, 4);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+ return;
+ } else
+#endif
+ if (m_sampleBuffer) {
+ //we need to get the sample data
+ uchar *data = 0;
+ m_sampleBuffer->GetPointer(&data);
+
+
+ //let's update the current image
+ if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12) {
+ convertYV12toRGB(data, m_size, m_currentImage,
+ m_brightness, m_contrast, m_hue, m_saturation);
+ } else if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YUY2) {
+ convertYUY2toRGB(data, m_size, m_currentImage,
+ m_brightness, m_contrast, m_hue, m_saturation);
+ } else if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_RGB32) {
+ normalizeRGB(data, m_size, m_currentImage);
+ }
+ m_sampleBuffer = ComPointer<IMediaSample>();
+ }
+
+ if (m_currentImage.isNull()) {
+ //we simply fill the whole video with content
+ //the callee has alrtead set the brush
+ painter.drawRect(r);
+ } else {
+ painter.drawImage(0, 0, m_currentImage);
+ }
+ }
+
+
+ void VideoRendererSoftFilter::normalizeRGB(const uchar *data, const QSize &s, QImage &destImage)
+ {
+ const int w = s.width(),
+ h = s.height();
+ if (destImage.size() != s) {
+ destImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ }
+ if (destImage.isNull()) {
+ return; //the system can't allocate the memory for the image drawing
+ }
+
+ const QRgb *rgb = reinterpret_cast<const QRgb*>(data);
+
+ //this sets the alpha channel to 0xff and flip the image vertically
+ for (int y = h - 1; y >= 0; --y) {
+ QRgb *dest = reinterpret_cast<QRgb*>(destImage.scanLine(y));
+ for(int i = w; i > 0; --i, ++rgb, ++dest) {
+ *dest = *rgb | (0xff << 24); //we force the alpha channel to 0xff
+ }
+ }
+ }
+
+
+ //we render data interpreted as YV12 into m_renderbuffer
+ void VideoRendererSoftFilter::convertYV12toRGB(const uchar *data, const QSize &s, QImage &destImage,
+ qreal brightness, qreal contrast, qreal hue, qreal saturation)
+ {
+ const int w = s.width(),
+ h = s.height();
+
+ //let's cache some computation
+ const int cosHx256 = qRound(qCos(hue) * contrast * saturation * 256),
+ sinHx256 = qRound(qSin(hue) * contrast * saturation * 256);
+
+ int Yvalue[256];
+ for(int i = 0;i<256;++i) {
+ Yvalue[i] = qRound(((i - 16) * contrast + brightness) * 298 + 128);
+ }
+
+
+ if (destImage.size() != s) {
+ destImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ }
+
+ if (destImage.isNull()) {
+ return; //the system can't allocate the memory for the image drawing
+ }
+
+ QRgb *dest = reinterpret_cast<QRgb*>(destImage.bits());
+ const uchar *dataY = data,
+ *dataV = data + (w*h),
+ *dataU = dataV + (w*h)/4;
+
+ uint *line1 = dest,
+ *line2 = dest + w;
+
+ for(int l = (h >> 1); l > 0; --l) {
+ //we treat 2 lines by 2 lines
+
+ for(int x = (w >> 1); x > 0; --x) {
+
+ const int u = *dataU++ - 128,
+ v = *dataV++ - 128;
+ const int d = (u * cosHx256 + v * sinHx256) >> 8,
+ e = (v * cosHx256 + u * sinHx256) >> 8;
+
+ const int compRed = 409 * e,
+ compGreen = -100 * d - 208 * e,
+ compBlue = 516 * d;
+
+ const int y21 = Yvalue[ dataY[w] ],
+ y11 = Yvalue[ *dataY++ ],
+ y22 = Yvalue[ dataY[w] ],
+ y12 = Yvalue[ *dataY++ ];
+
+ //1st line 1st pixel
+ *line1++ = CLIPPED_PIXEL(y11, compRed, compGreen, compBlue);
+
+ //1st line, 2nd pixel
+ *line1++ = CLIPPED_PIXEL(y12, compRed, compGreen, compBlue);
+
+ //2nd line 1st pixel
+ *line2++ = CLIPPED_PIXEL(y21, compRed, compGreen, compBlue);
+
+ //2nd line 2nd pixel
+ *line2++ = CLIPPED_PIXEL(y22, compRed, compGreen, compBlue);
+
+ } //for
+
+ //end of the line
+ dataY += w;
+ line1 = line2;
+ line2 += w;
+
+ } //for
+
+ }
+
+ //we render data interpreted as YUY2 into m_renderbuffer
+ void VideoRendererSoftFilter::convertYUY2toRGB(const uchar *data, const QSize &s, QImage &destImage,
+ qreal brightness, qreal contrast, qreal hue, qreal saturation)
+ {
+ const int w = s.width(),
+ h = s.height();
+
+ //let's cache some computation
+ int Yvalue[256];
+ for(int i = 0;i<256;++i) {
+ Yvalue[i] = qRound(((i - 16) * contrast + brightness) * 298 + 128);
+ }
+
+ const int cosHx256 = qRound(qCos(hue) * contrast * saturation * 256),
+ sinHx256 = qRound(qSin(hue) * contrast * saturation * 256);
+
+ if (destImage.size() != s) {
+ //this will only allocate memory when needed
+ destImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ }
+ if (destImage.isNull()) {
+ return; //the system can't allocate the memory for the image drawing
+ }
+
+ QRgb *dest = reinterpret_cast<QRgb*>(destImage.bits());
+
+ //the number of iterations is width * height / 2 because we treat 2 pixels at each iterations
+ for (int c = w * h / 2; c > 0 ; --c) {
+
+ //the idea of that algorithm comes from
+ //http://msdn2.microsoft.com/en-us/library/ms867704.aspx#yuvformats_identifying_yuv_formats_in_directshow
+
+ //we treat 2 pixels by 2 pixels (we start reading 2 pixels info ie. "YUYV"
+ const int y1 = Yvalue[*data++],
+ u = *data++ - 128,
+ y2 = Yvalue[*data++],
+ v = *data++ - 128;
+
+ const int d = (u * cosHx256 + v * sinHx256) >> 8,
+ e = (v * cosHx256 + u * sinHx256) >> 8;
+
+ const int compRed = 409 * e,
+ compGreen = -100 * d - 208 * e,
+ compBlue = 516 * d;
+
+ //first pixel
+ *dest++ = CLIPPED_PIXEL(y1, compRed, compGreen, compBlue);
+
+ //second pixel
+ *dest++ = CLIPPED_PIXEL(y2, compRed, compGreen, compBlue);
+ }
+ }
+
+
+ VideoRendererSoft::VideoRendererSoft(QWidget *target) :
+ m_renderer(new VideoRendererSoftFilter(this)), m_target(target)
+ {
+ m_filter = Filter(m_renderer);
+ }
+
+ VideoRendererSoft::~VideoRendererSoft()
+ {
+ }
+
+
+ bool VideoRendererSoft::isNative() const
+ {
+ return false;
+ }
+
+
+ void VideoRendererSoft::repaintCurrentFrame(QWidget *target, const QRect &rect)
+ {
+ QPainter painter(target);
+
+ QColor backColor = target->palette().color(target->backgroundRole());
+ painter.setBrush(backColor);
+ painter.setPen(Qt::NoPen);
+ if (!m_videoRect.contains(rect)) {
+ //we repaint the borders only when needed
+ const QVector<QRect> reg = (QRegion(rect) - m_videoRect).rects();
+ for (int i = 0; i < reg.count(); ++i) {
+ painter.drawRect(reg.at(i));
+ }
+ }
+
+ painter.setRenderHint(QPainter::SmoothPixmapTransform);
+ painter.setTransform(m_transform, true);
+ QSize vsize = videoSize();
+ m_renderer->repaintCurrentFrame(painter, QRect(0,0, vsize.width(), vsize.height()));
+ }
+
+ void VideoRendererSoft::notifyResize(const QSize &size,
+ Phonon::VideoWidget::AspectRatio aspectRatio, Phonon::VideoWidget::ScaleMode scaleMode)
+ {
+ const QSize vsize = videoSize();
+ internalNotifyResize(size, vsize, aspectRatio, scaleMode);
+
+ m_transform.reset();
+
+ if (vsize.isValid() && size.isValid()) {
+ m_transform.translate(m_dstX, m_dstY);
+ const qreal sx = qreal(m_dstWidth) / qreal(vsize.width()),
+ sy = qreal(m_dstHeight) / qreal(vsize.height());
+ m_transform.scale(sx, sy);
+ m_videoRect = m_transform.mapRect( QRect(0,0, vsize.width(), vsize.height()));
+ }
+ }
+
+ QSize VideoRendererSoft::videoSize() const
+ {
+ if (m_renderer->pins().first()->connected()) {
+ return m_renderer->videoSize();
+ } else {
+ return m_renderer->currentImage().size();
+ }
+ }
+
+ void VideoRendererSoft::applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation)
+ {
+ m_renderer->applyMixerSettings(brightness, contrast, hue, saturation);
+ }
+
+ QImage VideoRendererSoft::snapshot() const
+ {
+ return m_renderer->currentImage(); //not accurate (especially when using opengl...)
+ }
+
+ void VideoRendererSoft::setSnapshot(const QImage &image)
+ {
+ m_renderer->setCurrentImage(image);
+ }
+
+ bool VideoRendererSoft::event(QEvent *e)
+ {
+ if (e->type() == QEvent::UpdateRequest) {
+ m_target->update(m_videoRect);
+ return true;
+ }
+ return QObject::event(e);
+ }
+
+
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_PHONON_VIDEO
diff --git a/src/3rdparty/phonon/ds9/videorenderer_soft.h b/src/3rdparty/phonon/ds9/videorenderer_soft.h
new file mode 100644
index 0000000..e47bca6
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/videorenderer_soft.h
@@ -0,0 +1,68 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_VIDEORENDERER_SOFT_H
+#define PHONON_VIDEORENDERER_SOFT_H
+
+#include "abstractvideorenderer.h"
+
+QT_BEGIN_NAMESPACE
+#ifndef QT_NO_PHONON_VIDEO
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class VideoRendererSoftFilter;
+ //this class is used to render evrything in software (like in the Graphics View)
+ class VideoRendererSoft : public AbstractVideoRenderer,
+ public QObject //this is used to receive events
+ {
+ public:
+ VideoRendererSoft(QWidget *);
+ ~VideoRendererSoft();
+
+ //Implementation from AbstractVideoRenderer
+ void repaintCurrentFrame(QWidget *target, const QRect &rect);
+ void notifyResize(const QSize&, Phonon::VideoWidget::AspectRatio, Phonon::VideoWidget::ScaleMode);
+ QSize videoSize() const;
+ void applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation);
+ bool isNative() const;
+
+ QImage snapshot() const;
+ void setSnapshot(const QImage &);
+
+ protected:
+ bool event(QEvent *);
+
+ private:
+ VideoRendererSoftFilter *m_renderer;
+ QTransform m_transform;
+ QRect m_videoRect; //rectangle where the video is displayed
+ QWidget *m_target;
+ };
+ }
+}
+
+#endif //QT_NO_PHONON_VIDEO
+
+QT_END_NAMESPACE
+
+#endif
+
+
diff --git a/src/3rdparty/phonon/ds9/videorenderer_vmr9.cpp b/src/3rdparty/phonon/ds9/videorenderer_vmr9.cpp
new file mode 100644
index 0000000..298e9fa
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/videorenderer_vmr9.cpp
@@ -0,0 +1,333 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "videorenderer_vmr9.h"
+
+#ifndef QT_NO_PHONON_VIDEO
+
+#include <QtGui/QWidget>
+#include <QtGui/QPainter>
+#include <QtCore/QTimerEvent>
+
+#ifndef Q_OS_WINCE
+#include <d3d9.h>
+#include <vmr9.h>
+#else
+#include <uuids.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ VideoRendererVMR9::~VideoRendererVMR9()
+ {
+ }
+
+ bool VideoRendererVMR9::isNative() const
+ {
+ return true;
+ }
+
+
+#ifdef Q_OS_WINCE
+ VideoRendererVMR9::VideoRendererVMR9(QWidget *target) : m_target(target)
+ {
+ m_target->setAttribute(Qt::WA_PaintOnScreen, true);
+ m_filter = Filter(CLSID_VideoRenderer, IID_IBaseFilter);
+ }
+
+ QSize VideoRendererVMR9::videoSize() const
+ {
+ LONG w = 0,
+ h = 0;
+ ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo);
+ if (basic) {
+ basic->GetVideoSize( &w, &h);
+ }
+ return QSize(w, h);
+ }
+
+ void VideoRendererVMR9::repaintCurrentFrame(QWidget * /*target*/, const QRect & /*rect*/)
+ {
+ //nothing to do here: the renderer paints everything
+ }
+
+ void VideoRendererVMR9::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio,
+ Phonon::VideoWidget::ScaleMode scaleMode)
+ {
+ if (!isActive()) {
+ ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo);
+ if (basic) {
+ basic->SetDestinationPosition(0, 0, 0, 0);
+ }
+ return;
+ }
+
+ ComPointer<IVideoWindow> video(m_filter, IID_IVideoWindow);
+
+ OAHWND owner;
+ HRESULT hr = video->get_Owner(&owner);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ const OAHWND newOwner = reinterpret_cast<OAHWND>(m_target->winId());
+ if (owner != newOwner) {
+ video->put_Owner(newOwner);
+ video->put_MessageDrain(newOwner);
+ video->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
+ }
+
+ //make sure the widget takes the whole size of the parent
+ video->SetWindowPosition(0, 0, size.width(), size.height());
+
+ const QSize vsize = videoSize();
+ internalNotifyResize(size, vsize, aspectRatio, scaleMode);
+
+ ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo);
+ if (basic) {
+ basic->SetDestinationPosition(m_dstX, m_dstY, m_dstWidth, m_dstHeight);
+ }
+ }
+
+ void VideoRendererVMR9::applyMixerSettings(qreal /*brightness*/, qreal /*contrast*/, qreal /*m_hue*/, qreal /*saturation*/)
+ {
+ //this can't be supported for WinCE
+ }
+
+ QImage VideoRendererVMR9::snapshot() const
+ {
+ ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo);
+ if (basic) {
+ LONG bufferSize = 0;
+ //1st we get the buffer size
+ basic->GetCurrentImage(&bufferSize, 0);
+
+ QByteArray buffer;
+ buffer.resize(bufferSize);
+ HRESULT hr = basic->GetCurrentImage(&bufferSize, reinterpret_cast<long*>(buffer.data()));
+
+ if (SUCCEEDED(hr)) {
+
+ const BITMAPINFOHEADER *bmi = reinterpret_cast<const BITMAPINFOHEADER*>(buffer.constData());
+
+ const int w = qAbs(bmi->biWidth),
+ h = qAbs(bmi->biHeight);
+
+ // Create image and copy data into image.
+ QImage ret(w, h, QImage::Format_RGB32);
+
+ if (!ret.isNull()) {
+ const char *data = buffer.constData() + bmi->biSize;
+ const int bytes_per_line = w * sizeof(QRgb);
+ for (int y = h - 1; y >= 0; --y) {
+ qMemCopy(ret.scanLine(y), //destination
+ data, //source
+ bytes_per_line);
+ data += bytes_per_line;
+ }
+ }
+ return ret;
+ }
+ }
+ return QImage();
+ }
+
+#else
+ VideoRendererVMR9::VideoRendererVMR9(QWidget *target) : m_target(target)
+ {
+ m_filter = Filter(CLSID_VideoMixingRenderer9, IID_IBaseFilter);
+ if (!m_filter) {
+ qWarning("the video widget could not be initialized correctly");
+ return;
+ }
+
+ ComPointer<IVMRFilterConfig9> config(m_filter, IID_IVMRFilterConfig9);
+ Q_ASSERT(config);
+ HRESULT hr = config->SetRenderingMode(VMR9Mode_Windowless);
+ Q_ASSERT(SUCCEEDED(hr));
+ hr = config->SetNumberOfStreams(1); //for now we limit it to 1 input stream
+ Q_ASSERT(SUCCEEDED(hr));
+ ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
+ windowlessControl->SetVideoClippingWindow(reinterpret_cast<HWND>(target->winId()));
+ }
+
+ QImage VideoRendererVMR9::snapshot() const
+ {
+ ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
+ if (windowlessControl) {
+ BYTE *buffer = 0;
+ HRESULT hr = windowlessControl->GetCurrentImage(&buffer);
+ if (SUCCEEDED(hr)) {
+
+ const BITMAPINFOHEADER *bmi = reinterpret_cast<BITMAPINFOHEADER*>(buffer);
+ const int w = qAbs(bmi->biWidth),
+ h = qAbs(bmi->biHeight);
+
+ // Create image and copy data into image.
+ QImage ret(w, h, QImage::Format_RGB32);
+
+ if (!ret.isNull()) {
+ uchar *data = buffer + bmi->biSize;
+ const int bytes_per_line = w * sizeof(QRgb);
+ for (int y = h - 1; y >= 0; --y) {
+ qMemCopy(ret.scanLine(y), //destination
+ data, //source
+ bytes_per_line);
+ data += bytes_per_line;
+ }
+ }
+ ::CoTaskMemFree(buffer);
+ return ret;
+ }
+ }
+ return QImage();
+ }
+
+ QSize VideoRendererVMR9::videoSize() const
+ {
+ LONG w = 0,
+ h = 0;
+ ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
+ if (windowlessControl) {
+ windowlessControl->GetNativeVideoSize( &w, &h, 0, 0);
+ }
+ return QSize(w, h);
+ }
+
+ void VideoRendererVMR9::repaintCurrentFrame(QWidget *target, const QRect &rect)
+ {
+ HDC hDC = target->getDC();
+ // repaint the video
+ ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
+
+ HRESULT hr = windowlessControl ? windowlessControl->RepaintVideo(target->winId(), hDC) : E_POINTER;
+ if (FAILED(hr) || m_dstY > 0 || m_dstX > 0) {
+ const QColor c = target->palette().color(target->backgroundRole());
+ COLORREF color = RGB(c.red(), c.green(), c.blue());
+ HPEN hPen = ::CreatePen(PS_SOLID, 1, color);
+ HBRUSH hBrush = ::CreateSolidBrush(color);
+ ::SelectObject(hDC, hPen);
+ ::SelectObject(hDC, hBrush);
+ // repaint the video
+ if (FAILED(hr)) {
+ //black background : we use the Win32 API to avoid the ghost effect of the backing store
+ ::Rectangle(hDC, 0, 0, target->width(), target->height());
+ } else {
+ if (m_dstY > 0) {
+ ::Rectangle(hDC, 0, 0, target->width(), m_dstY); //top
+ ::Rectangle(hDC, 0, target->height() - m_dstY, target->width(), target->height()); //bottom
+ }
+ if (m_dstX > 0) {
+ ::Rectangle(hDC, 0, m_dstY, m_dstX, m_dstHeight + m_dstY); //left
+ ::Rectangle(hDC, m_dstWidth + m_dstX, m_dstY, target->width(), m_dstHeight + m_dstY); //right
+ }
+ }
+ ::DeleteObject(hPen);
+ ::DeleteObject(hBrush);
+ }
+ target->releaseDC(hDC);
+
+ }
+
+ void VideoRendererVMR9::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio,
+ Phonon::VideoWidget::ScaleMode scaleMode)
+ {
+ if (!isActive()) {
+ RECT dummyRect = { 0, 0, 0, 0};
+ ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
+ windowlessControl->SetVideoPosition(&dummyRect, &dummyRect);
+ return;
+ }
+
+
+ const QSize vsize = videoSize();
+ internalNotifyResize(size, vsize, aspectRatio, scaleMode);
+
+ RECT dstRectWin = { m_dstX, m_dstY, m_dstWidth + m_dstX, m_dstHeight + m_dstY};
+ RECT srcRectWin = { 0, 0, vsize.width(), vsize.height()};
+
+ ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
+ if (windowlessControl) {
+ windowlessControl->SetVideoPosition(&srcRectWin, &dstRectWin);
+ }
+ }
+
+ void VideoRendererVMR9::applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation)
+ {
+ InputPin sink = BackendNode::pins(m_filter, PINDIR_INPUT).first();
+ OutputPin source;
+ if (FAILED(sink->ConnectedTo(source.pparam()))) {
+ return; //it must be connected to work
+ }
+
+ //get the mixer (used for brightness/contrast/saturation/hue)
+ ComPointer<IVMRMixerControl9> mixer(m_filter, IID_IVMRMixerControl9);
+ Q_ASSERT(mixer);
+
+ VMR9ProcAmpControl ctrl;
+ ctrl.dwSize = sizeof(ctrl);
+ ctrl.dwFlags = ProcAmpControl9_Contrast | ProcAmpControl9_Brightness | ProcAmpControl9_Saturation | ProcAmpControl9_Hue;
+ VMR9ProcAmpControlRange range;
+ range.dwSize = sizeof(range);
+
+ range.dwProperty = ProcAmpControl9_Contrast;
+ HRESULT hr = mixer->GetProcAmpControlRange(0, &range);
+ if (FAILED(hr)) {
+ return;
+ }
+ ctrl.Contrast = ((contrast < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(contrast) + range.DefaultValue;
+
+ //brightness
+ range.dwProperty = ProcAmpControl9_Brightness;
+ hr = mixer->GetProcAmpControlRange(0, &range);
+ if (FAILED(hr)) {
+ return;
+ }
+ ctrl.Brightness = ((brightness < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(brightness) + range.DefaultValue;
+
+ //saturation
+ range.dwProperty = ProcAmpControl9_Saturation;
+ hr = mixer->GetProcAmpControlRange(0, &range);
+ if (FAILED(hr)) {
+ return;
+ }
+ ctrl.Saturation = ((saturation < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(saturation) + range.DefaultValue;
+
+ //hue
+ range.dwProperty = ProcAmpControl9_Hue;
+ hr = mixer->GetProcAmpControlRange(0, &range);
+ if (FAILED(hr)) {
+ return;
+ }
+ ctrl.Hue = ((hue < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(hue) + range.DefaultValue;
+
+ //finally set the settings
+ mixer->SetProcAmpControl(0, &ctrl);
+ }
+#endif
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_PHONON_VIDEO
diff --git a/src/3rdparty/phonon/ds9/videorenderer_vmr9.h b/src/3rdparty/phonon/ds9/videorenderer_vmr9.h
new file mode 100644
index 0000000..4eb237e
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/videorenderer_vmr9.h
@@ -0,0 +1,56 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_VIDEORENDERER_VMR9_H
+#define PHONON_VIDEORENDERER_VMR9_H
+
+#include "abstractvideorenderer.h"
+#include "compointer.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_VIDEO
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class VideoRendererVMR9 : public AbstractVideoRenderer
+ {
+ public:
+ VideoRendererVMR9(QWidget *target);
+ ~VideoRendererVMR9();
+
+ //Implementation from AbstractVideoRenderer
+ void repaintCurrentFrame(QWidget *target, const QRect &rect);
+ void notifyResize(const QSize&, Phonon::VideoWidget::AspectRatio, Phonon::VideoWidget::ScaleMode);
+ QSize videoSize() const;
+ QImage snapshot() const;
+ void applyMixerSettings(qreal brightness, qreal contrast, qreal m_hue, qreal saturation);
+ bool isNative() const;
+ private:
+ QWidget *m_target;
+ };
+ }
+}
+
+#endif //QT_NO_PHONON_VIDEO
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/3rdparty/phonon/ds9/videowidget.cpp b/src/3rdparty/phonon/ds9/videowidget.cpp
new file mode 100644
index 0000000..de7ce5f
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/videowidget.cpp
@@ -0,0 +1,397 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "videowidget.h"
+
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEvent>
+#include <QtCore/QTimer>
+#include <QtCore/QSettings>
+
+#include "mediaobject.h"
+
+#include "videorenderer_vmr9.h"
+#include "videorenderer_soft.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_VIDEO
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ //class used internally to return the widget where the video is shown on
+ class VideoWindow : public QWidget
+ {
+ public:
+ explicit VideoWindow(QWidget *parent, VideoWidget *vw)
+ : QWidget(parent), m_node(vw), m_currentRenderer(0)
+ {
+ //default background color
+ setPalette(QPalette(Qt::black));
+ setAttribute(Qt::WA_OpaquePaintEvent, true);
+ setAttribute(Qt::WA_NoSystemBackground, true);
+ setAttribute(Qt::WA_PaintOnScreen, true);
+ setAutoFillBackground(false);
+ }
+
+ QPaintEngine* paintEngine() const
+ {
+ return 0;
+ }
+
+ bool isEmbedded() const
+ {
+#if QT_VERSION >= 0x040400
+ return window()->testAttribute(Qt::WA_DontShowOnScreen);
+#else
+ return false;
+#endif
+ }
+
+ bool needsSoftRendering() const
+ {
+ QPaintDevice *dev = QPainter::redirected(this, 0);
+ return (dev && dev != this);
+ }
+
+ void resizeEvent(QResizeEvent *e)
+ {
+ m_node->updateVideoSize();
+ QWidget::resizeEvent(e);
+ }
+
+ AbstractVideoRenderer *currentRenderer() const
+ {
+ return m_currentRenderer;
+ }
+
+ void setCurrentRenderer(AbstractVideoRenderer *renderer)
+ {
+ m_currentRenderer = renderer;
+ update();
+ }
+
+ QSize sizeHint() const
+ {
+ return m_currentRenderer->sizeHint().expandedTo(QWidget::sizeHint());
+ }
+
+ void changeEvent(QEvent *e)
+ {
+ checkCurrentRenderingMode();
+ QWidget::changeEvent(e);
+ }
+
+ void setVisible(bool visible)
+ {
+ checkCurrentRenderingMode();
+ QWidget::setVisible(visible);
+ }
+
+ void paintEvent(QPaintEvent *e)
+ {
+ checkCurrentRenderingMode();
+ m_currentRenderer->repaintCurrentFrame(this, e->rect());
+ }
+
+ //this code manages the activation/deactivation of the screensaver
+ /*bool event(QEvent *e)
+ {
+ if (e->type() == QEvent::Resize) {
+ //we disable the screensaver if the video is in fullscreen mode
+ disableScreenSaver(window()->windowState() & Qt::WindowFullScreen);
+ }
+ return QWidget::event(e);
+ }*/
+
+ private:
+ //for fullscreen mode
+ void disableScreenSaver(bool b)
+ {
+ const QLatin1String screenSaverActive("ScreenSaveActive");
+ QSettings settings( QLatin1String("HKEY_CURRENT_USER\\Control Panel\\Desktop"), QSettings::NativeFormat);
+ if (b) {
+ if (m_restoreScreenSaverActive.isNull()) {
+ //we store the value to be able to restore it later
+ m_restoreScreenSaverActive = settings.value(screenSaverActive);
+ settings.setValue(screenSaverActive, QString::number(!b));
+ }
+ } else if (!m_restoreScreenSaverActive.isNull()) {
+ //we restore the previous value
+ settings.setValue(screenSaverActive, m_restoreScreenSaverActive);
+ }
+ }
+
+ void checkCurrentRenderingMode()
+ {
+ if (!m_currentRenderer)
+ return;
+
+ if (m_currentRenderer->isNative()) {
+ if (isEmbedded()) {
+ //we need to switch to software renderer
+ m_currentRenderer = m_node->switchRendering(m_currentRenderer);
+ setAttribute(Qt::WA_PaintOnScreen, false);
+ } else if (needsSoftRendering()) {
+ m_node->performSoftRendering(m_currentRenderer->snapshot());
+ }
+ } else if (!isEmbedded()) {
+ m_currentRenderer = m_node->switchRendering(m_currentRenderer);
+ setAttribute(Qt::WA_PaintOnScreen, true);
+ }
+ }
+
+ VideoWidget *m_node;
+ AbstractVideoRenderer *m_currentRenderer;
+ QVariant m_restoreScreenSaverActive;
+ };
+
+ VideoWidget::VideoWidget(QWidget *parent)
+ : BackendNode(parent), m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto),
+ m_scaleMode(Phonon::VideoWidget::FitInView),
+ m_brightness(0.), m_contrast(0.), m_hue(0.), m_saturation(0.), m_noNativeRendererSupported(false)
+
+ {
+ //initialisation of the widget
+ m_widget = new VideoWindow(parent, this);
+
+ //initialization of the renderers
+ qMemSet(m_renderers, 0, sizeof(m_renderers));
+
+ for(int i = 0; i< FILTER_COUNT ;++i) {
+ //This might return a non native (ie Qt) renderer in case native is not supported
+ AbstractVideoRenderer *renderer = getRenderer(i, Native, true);
+ m_filters[i] = renderer->getFilter();
+ }
+
+ //by default, we take the first VideoWindow object
+ setCurrentGraph(0);
+ }
+
+ VideoWidget::~VideoWidget()
+ {
+ for (int i = 0; i < 4; ++i) {
+ delete m_renderers[i];
+ }
+ }
+
+ void VideoWidget::notifyVideoLoaded()
+ {
+ updateVideoSize();
+ m_widget->updateGeometry();
+ }
+
+ AbstractVideoRenderer *VideoWidget::switchRendering(AbstractVideoRenderer *current)
+ {
+ const bool toNative = !current->isNative();
+ if (toNative && m_noNativeRendererSupported)
+ return current; //no switch here
+
+ //firt we delete the renderer
+ //initialization of the widgets
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ Filter oldFilter = m_filters[i];
+
+ //Let's create a software renderer
+ AbstractVideoRenderer *renderer = getRenderer(i, toNative ? Native : NonNative, true);
+
+ if (m_mediaObject) {
+ m_mediaObject->switchFilters(i, oldFilter, renderer->getFilter());
+ }
+
+ m_filters[i] = renderer->getFilter();
+ }
+
+ return getRenderer(mediaObject()->currentGraph()->index(), toNative ? Native: NonNative);
+ }
+
+ void VideoWidget::performSoftRendering(const QImage &currentImage)
+ {
+ const int graphIndex = mediaObject()->currentGraph()->index();
+ VideoRendererSoft *r = static_cast<VideoRendererSoft*>(getRenderer(graphIndex, NonNative, true /*autocreation*/));
+ r->setSnapshot(currentImage);
+ r->notifyResize(m_widget->size(), m_aspectRatio, m_scaleMode);
+ r->repaintCurrentFrame(m_widget, m_widget->rect());
+
+ }
+
+ void VideoWidget::setCurrentGraph(int index)
+ {
+ for(int i = 0; i < 2; ++i) {
+ if (AbstractVideoRenderer *renderer = getRenderer(i, Native))
+ renderer->setActive(index == i);
+ }
+
+ //be sure to update all the things that needs an update
+ applyMixerSettings();
+ updateVideoSize();
+
+ AbstractVideoRenderer *r = m_widget->currentRenderer();
+
+ //we determine dynamically if it is native or non native
+ r = getRenderer(index, !r || r->isNative() ? Native : NonNative);
+ if (!r)
+ r = getRenderer(index, NonNative);
+ m_widget->setCurrentRenderer(r);
+ }
+
+
+ Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const
+ {
+ return m_aspectRatio;
+ }
+
+ void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio)
+ {
+ m_aspectRatio = aspectRatio;
+ updateVideoSize();
+ }
+
+ Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const
+ {
+ return m_scaleMode;
+ }
+
+
+ QWidget *VideoWidget::widget()
+ {
+ return m_widget;
+ }
+
+
+ void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode)
+ {
+ m_scaleMode = scaleMode;
+ updateVideoSize();
+ }
+
+ void VideoWidget::setBrightness(qreal b)
+ {
+ m_brightness = b;
+ applyMixerSettings();
+ }
+
+ void VideoWidget::setContrast(qreal c)
+ {
+ m_contrast = c;
+ applyMixerSettings();
+ }
+
+ void VideoWidget::setHue(qreal h)
+ {
+ m_hue = h;
+ applyMixerSettings();
+ }
+
+ void VideoWidget::setSaturation(qreal s)
+ {
+ m_saturation = s;
+ applyMixerSettings();
+ }
+
+ qreal VideoWidget::brightness() const
+ {
+ return m_brightness;
+ }
+
+
+ qreal VideoWidget::contrast() const
+ {
+ return m_contrast;
+ }
+
+ qreal VideoWidget::hue() const
+ {
+ return m_hue;
+ }
+
+ qreal VideoWidget::saturation() const
+ {
+ return m_saturation;
+ }
+
+
+ AbstractVideoRenderer *VideoWidget::getRenderer(int graphIndex, RendererType type, bool autoCreate)
+ {
+ int index = graphIndex * 2 + type;
+ if (m_renderers[index] == 0 && autoCreate) {
+ AbstractVideoRenderer *renderer = 0;
+ if (type == Native) {
+ renderer = new VideoRendererVMR9(m_widget);
+ if (renderer->getFilter() == 0) {
+ //instanciating the renderer might fail with error VFW_E_DDRAW_CAPS_NOT_SUITABLE (0x80040273)
+ m_noNativeRendererSupported = true;
+ delete renderer;
+ renderer = 0;
+ }
+ }
+
+ if (renderer == 0) {
+ type = NonNative;
+ index = graphIndex * 2 + type;
+ if (m_renderers[index] == 0)
+ renderer = new VideoRendererSoft(m_widget); //this always succeeds
+ else
+ renderer = m_renderers[index];
+ }
+
+ m_renderers[index] = renderer;
+
+ //be sure to update all the things that needs an update
+ applyMixerSettings();
+ updateVideoSize();
+
+ }
+ return m_renderers[index];
+ }
+
+ //this must be called whe nthe node is actually connected
+ void VideoWidget::applyMixerSettings() const
+ {
+ for (int i = 0; i < 4; ++i) {
+ if (AbstractVideoRenderer *renderer = m_renderers[i])
+ renderer->applyMixerSettings(m_brightness, m_contrast, m_hue, m_saturation);
+ }
+ }
+
+ void VideoWidget::connected(BackendNode *, const InputPin&)
+ {
+ //in case of a connection, we simply reapply the mixer settings
+ applyMixerSettings();
+ updateVideoSize();
+ }
+
+ void VideoWidget::updateVideoSize() const
+ {
+ for (int i = 0; i < 4; ++i) {
+ if (AbstractVideoRenderer *renderer = m_renderers[i])
+ renderer->notifyResize(m_widget->size(), m_aspectRatio, m_scaleMode);
+ }
+ }
+
+
+
+ }
+}
+
+#endif //QT_NO_PHONON_VIDEO
+
+QT_END_NAMESPACE
+
+#include "moc_videowidget.cpp"
diff --git a/src/3rdparty/phonon/ds9/videowidget.h b/src/3rdparty/phonon/ds9/videowidget.h
new file mode 100644
index 0000000..fc8a6e3
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/videowidget.h
@@ -0,0 +1,96 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_VIDEOWIDGET_H
+#define PHONON_VIDEOWIDGET_H
+
+#include <QtGui/QWidget>
+#include <phonon/videowidgetinterface.h>
+#include "backendnode.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_VIDEO
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class VideoWindow;
+ class AbstractVideoRenderer;
+
+ class VideoWidget : public BackendNode, public Phonon::VideoWidgetInterface
+ {
+ enum RendererType
+ {
+ Native = 0,
+ NonNative = 1
+ };
+
+ Q_OBJECT
+ Q_INTERFACES(Phonon::VideoWidgetInterface)
+ public:
+ VideoWidget(QWidget *parent = 0);
+ ~VideoWidget();
+
+ Phonon::VideoWidget::AspectRatio aspectRatio() const;
+ void setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio);
+ Phonon::VideoWidget::ScaleMode scaleMode() const;
+ void setScaleMode(Phonon::VideoWidget::ScaleMode);
+ qreal brightness() const;
+ void setBrightness(qreal);
+ qreal contrast() const;
+ void setContrast(qreal);
+ qreal hue() const;
+ void setHue(qreal);
+ qreal saturation() const;
+ void setSaturation(qreal);
+
+ void setCurrentGraph(int index);
+
+ QWidget *widget();
+
+ void notifyVideoLoaded();
+ AbstractVideoRenderer *switchRendering(AbstractVideoRenderer *current);
+ void performSoftRendering(const QImage &currentImage);
+
+ //apply contrast/brightness/hue/saturation
+ void applyMixerSettings() const;
+ void updateVideoSize() const;
+
+ protected:
+ virtual void connected(BackendNode *, const InputPin& inpin);
+
+ private:
+ AbstractVideoRenderer *getRenderer(int graphIndex, RendererType type, bool autoCreate = false);
+
+ Phonon::VideoWidget::AspectRatio m_aspectRatio;
+ Phonon::VideoWidget::ScaleMode m_scaleMode;
+
+ VideoWindow *m_widget;
+ qreal m_brightness, m_contrast, m_hue, m_saturation;
+ AbstractVideoRenderer* m_renderers[4];
+ mutable bool m_noNativeRendererSupported;
+ };
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // PHONON_VIDEOWIDGET_H
+
+#endif //QT_NO_PHONON_VIDEO
diff --git a/src/3rdparty/phonon/ds9/volumeeffect.cpp b/src/3rdparty/phonon/ds9/volumeeffect.cpp
new file mode 100644
index 0000000..2fd1afc
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/volumeeffect.cpp
@@ -0,0 +1,296 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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 "volumeeffect.h"
+#include "qbasefilter.h"
+#include "qmeminputpin.h"
+
+#include <QtCore/qmath.h> //for sqrt
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
+
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ /**************************************************************************
+ * curve functions
+ *************************************************************************/
+
+ static qreal curveValueFadeIn3dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
+ {
+ return (fadeStart + fadeDiff * sqrt(completed));
+ }
+ static qreal curveValueFadeOut3dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
+ {
+ return (fadeStart + fadeDiff * (1.0 - sqrt(1.0 - completed)));
+ }
+ // in == out for a linear fade
+ static qreal curveValueFade6dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
+ {
+ return (fadeStart + fadeDiff * completed);
+ }
+ static qreal curveValueFadeIn9dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
+ {
+ return (fadeStart + fadeDiff * pow(completed, 1.5));
+ }
+ static qreal curveValueFadeOut9dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
+ {
+ return (fadeStart + fadeDiff * (1.0 - pow(1.0 - completed, 1.5)));
+ }
+ static qreal curveValueFadeIn12dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
+ {
+ return (fadeStart + fadeDiff * completed * completed);
+ }
+ static qreal curveValueFadeOut12dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
+ {
+ const qreal x = 1.0 - completed;
+ return (fadeStart + fadeDiff * (1.0 - x * x));
+ }
+
+ static const QVector<AM_MEDIA_TYPE> audioMediaType()
+ {
+ QVector<AM_MEDIA_TYPE> ret;
+
+ AM_MEDIA_TYPE mt;
+ mt.majortype = MEDIATYPE_Audio;
+ mt.subtype = MEDIASUBTYPE_PCM;
+ mt.bFixedSizeSamples = 1;
+ mt.bTemporalCompression = 0;
+ mt.pUnk = 0;
+ mt.lSampleSize = 1;
+ mt.cbFormat = 0;
+ mt.pbFormat = 0;
+ mt.formattype = GUID_NULL;
+ ret << mt;
+ return ret;
+ }
+
+ class VolumeMemInputPin : public QMemInputPin
+ {
+ public:
+ VolumeMemInputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt) : QMemInputPin(parent, mt, true /*transform*/)
+ {
+ }
+
+ ~VolumeMemInputPin()
+ {
+ }
+
+ STDMETHODIMP NotifyAllocator(IMemAllocator *alloc, BOOL b)
+ {
+ ALLOCATOR_PROPERTIES prop;
+ HRESULT hr = alloc->GetProperties(&prop);
+ if (SUCCEEDED(hr) && prop.cBuffers > 1) {
+ //this allows to reduce the latency for sound
+ //the problem is that too low numbers makes the whole thing fail...
+ ALLOCATOR_PROPERTIES actual;
+ prop.cBuffers = 1;
+ alloc->SetProperties(&prop, &actual);
+ }
+ return QMemInputPin::NotifyAllocator(alloc, b);
+ }
+
+ };
+
+ class VolumeMemOutputPin : public QPin
+ {
+ public:
+ VolumeMemOutputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt) : QPin(parent, PINDIR_OUTPUT, mt)
+ {
+ }
+
+ ~VolumeMemOutputPin()
+ {
+ }
+
+ };
+
+ class VolumeEffectFilter : public QBaseFilter
+ {
+ public:
+ VolumeEffectFilter(VolumeEffect *);
+
+ //reimplementation
+ virtual HRESULT processSample(IMediaSample *);
+
+ private:
+ void treatOneSamplePerChannel(BYTE **buffer, int sampleSize, int channelCount, int frequency);
+
+ QMemInputPin *m_input;
+ QPin *m_output;
+ VolumeEffect *m_volumeEffect;
+ };
+
+ VolumeEffectFilter::VolumeEffectFilter(VolumeEffect *ve) : QBaseFilter(CLSID_NULL),
+ m_volumeEffect(ve)
+ {
+ QVector<AM_MEDIA_TYPE> mt;
+
+ //creating the output
+ m_output = new VolumeMemOutputPin(this, mt);
+
+ //then creating the input
+ mt << audioMediaType();
+ m_input = new VolumeMemInputPin(this, mt);
+ m_input->addOutput(m_output); //make the connection here
+ }
+
+ void VolumeEffectFilter::treatOneSamplePerChannel(BYTE **buffer, int sampleSize, int channelCount, int frequency)
+ {
+ float volume = m_volumeEffect->volume();
+ if (m_volumeEffect->m_fading) {
+ const qreal lastSample = m_volumeEffect->m_fadeDuration * frequency / 1000;
+ const qreal completed = qreal(m_volumeEffect->m_fadeSamplePosition++) / lastSample;
+
+ if (qFuzzyCompare(completed, qreal(1.))) {
+ m_volumeEffect->setVolume(m_volumeEffect->m_targetVolume);
+ m_volumeEffect->m_fading = false; //we end the fading effect
+ } else {
+ volume = m_volumeEffect->m_fadeCurveFn(m_volumeEffect->m_initialVolume,
+ m_volumeEffect->m_targetVolume - m_volumeEffect->m_initialVolume,
+ completed);
+ }
+ }
+
+ for(int c = 0; c < channelCount; ++c) {
+ switch (sampleSize)
+ {
+ case 2:
+ {
+ short *shortBuffer = reinterpret_cast<short*>(*buffer);
+ *shortBuffer *= qRound(volume);
+ }
+ break;
+ case 1:
+ **buffer *= qRound(volume);
+ break;
+ default:
+ break;
+ }
+
+ *buffer += sampleSize;
+ }
+ }
+
+ HRESULT VolumeEffectFilter::processSample(IMediaSample * ms)
+ {
+ BYTE *buffer = 0;
+ ms->GetPointer(&buffer);
+ if (buffer) {
+ const AM_MEDIA_TYPE &mt = m_output->connectedType();
+ if (mt.formattype != FORMAT_WaveFormatEx) {
+ return VFW_E_INVALIDMEDIATYPE;
+ }
+ WAVEFORMATEX *format = reinterpret_cast<WAVEFORMATEX*>(mt.pbFormat);
+ const int channelCount = format->nChannels;
+ const int sampleSize = format->wBitsPerSample / 8; //...in bytes
+
+
+ const BYTE *end = buffer + ms->GetActualDataLength();
+ while (buffer < end) {
+ treatOneSamplePerChannel(&buffer, sampleSize, channelCount, format->nSamplesPerSec);
+ }
+ }
+
+ return S_OK;
+ }
+
+ VolumeEffect::VolumeEffect(QObject *parent) : Effect(parent),
+ m_volume(1), m_fadeCurve(Phonon::VolumeFaderEffect::Fade3Decibel),
+ m_fading(false), m_initialVolume(0), m_targetVolume(0), m_fadeDuration(0),
+ m_fadeSamplePosition(0)
+ {
+ //creation of the effects
+ for(int i = 0; i < FILTER_COUNT; ++i) {
+ VolumeEffectFilter *f = new VolumeEffectFilter(this);
+ m_filters[i] = Filter(f);
+ }
+ }
+
+ float VolumeEffect::volume() const
+ {
+ return m_volume;
+ }
+
+ void VolumeEffect::setVolume(float newVolume)
+ {
+ m_volume = newVolume;
+ }
+
+ Phonon::VolumeFaderEffect::FadeCurve VolumeEffect::fadeCurve() const
+ {
+ return m_fadeCurve;
+ }
+
+ void VolumeEffect::setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve curve)
+ {
+ m_fadeCurve = curve;
+ }
+
+
+ void VolumeEffect::fadeTo(float vol, int duration)
+ {
+ m_fading = true; //will be set back to false when fading is finished
+ m_targetVolume = vol;
+ m_fadeSamplePosition = 0;
+ m_initialVolume = m_volume;
+ m_fadeDuration = duration;
+
+ //in or out?
+ const bool in = vol > m_volume;
+
+ switch(m_fadeCurve)
+ {
+ case Phonon::VolumeFaderEffect::Fade6Decibel:
+ m_fadeCurveFn = curveValueFade6dB;
+ break;
+ case Phonon::VolumeFaderEffect::Fade9Decibel:
+ if (in) {
+ m_fadeCurveFn = curveValueFadeIn9dB;
+ } else {
+ m_fadeCurveFn = curveValueFadeOut9dB;
+ }
+ break;
+ case Phonon::VolumeFaderEffect::Fade12Decibel:
+ if (in) {
+ m_fadeCurveFn = curveValueFadeIn12dB;
+ } else {
+ m_fadeCurveFn = curveValueFadeOut12dB;
+ }
+ break;
+ case Phonon::VolumeFaderEffect::Fade3Decibel:
+ default:
+ if (in) {
+ m_fadeCurveFn = curveValueFadeIn3dB;
+ } else {
+ m_fadeCurveFn = curveValueFadeOut3dB;
+ }
+ break;
+ }
+ }
+ }
+}
+
+#endif //QT_NO_PHONON_VOLUMEFADEREFFECT
+
+QT_END_NAMESPACE
+
+#include "moc_volumeeffect.cpp"
diff --git a/src/3rdparty/phonon/ds9/volumeeffect.h b/src/3rdparty/phonon/ds9/volumeeffect.h
new file mode 100644
index 0000000..d1b0186
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/volumeeffect.h
@@ -0,0 +1,71 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+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 or 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_VOLUMEEFFECT_H
+#define PHONON_VOLUMEEFFECT_H
+
+#include "effect.h"
+#include <phonon/volumefaderinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ class VolumeEffectFilter;
+ class VolumeEffect : public Effect, public Phonon::VolumeFaderInterface
+ {
+ Q_OBJECT
+ Q_INTERFACES(Phonon::VolumeFaderInterface)
+ public:
+ VolumeEffect(QObject *parent);
+
+ //reimplementation
+ virtual float volume() const;
+ virtual void setVolume(float);
+ virtual Phonon::VolumeFaderEffect::FadeCurve fadeCurve() const;
+ virtual void setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve);
+ virtual void fadeTo(float, int);
+
+ private:
+ float m_volume;
+
+ //paramaters used to fade
+ Phonon::VolumeFaderEffect::FadeCurve m_fadeCurve;
+
+ bool m_fading; //determines if we should be fading.
+ float m_initialVolume;
+ float m_targetVolume;
+ int m_fadeDuration;
+ int m_fadeSamplePosition;
+ qreal (*m_fadeCurveFn)(const qreal, const qreal, const qreal);
+
+ //allow the filter to get access to that
+ friend class VolumeEffectFilter;
+
+ };
+ }
+}
+
+#endif //QT_NO_PHONON_VOLUMEFADEREFFECT
+
+QT_END_NAMESPACE
+
+#endif