diff options
Diffstat (limited to 'src/3rdparty/phonon/ds9')
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( ¶mCount); + + 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(¤t, &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(¤t, 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, ¶m1, ¶m2, 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(¤tTime); + 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> ¶ms) + { + 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> ¶ms); +#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 ¤t = 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 ¤t = m_mediaTypes.at(i); + setConnectedType(current); + HRESULT hr = pin->ReceiveConnection(this, ¤t); + 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(¤t); + + 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 ¤tImage) + { + 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 ¤tImage); + + //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 |