src/3rdparty/phonon/ds9/mediagraph.cpp
changeset 0 1918ee327afb
child 30 5dc02b23752f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/3rdparty/phonon/ds9/mediagraph.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1101 @@
+/*  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();
+                if (info.pGraph == m_graph)
+                    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 (HRESULT(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);
+
+#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()) == HRESULT(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::fromWCharArray(str));
+                    SysFreeString(str);
+                }
+                hr = mediaContent->get_Title(&str);
+                if (SUCCEEDED(hr)) {
+                    ret.insert(QLatin1String("TITLE"), QString::fromWCharArray(str));
+                    SysFreeString(str);
+                }
+                hr = mediaContent->get_Description(&str);
+                if (SUCCEEDED(hr)) {
+                    ret.insert(QLatin1String("DESCRIPTION"), QString::fromWCharArray(str));
+                    SysFreeString(str);
+                }
+                hr = mediaContent->get_Copyright(&str);
+                if (SUCCEEDED(hr)) {
+                    ret.insert(QLatin1String("COPYRIGHT"), QString::fromWCharArray(str));
+                    SysFreeString(str);
+                }
+                hr = mediaContent->get_MoreInfoText(&str);
+                if (SUCCEEDED(hr)) {
+                    ret.insert(QLatin1String("MOREINFO"), QString::fromWCharArray(str));
+                    SysFreeString(str);
+                }
+            }
+            return ret;
+        }
+
+        Filter MediaGraph::realSource() const
+        {
+            return m_realSource;
+        }
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+        void MediaGraph::setStopPosition(qint64 time)
+        {
+            qint64 current = 0,
+                stop = 0;
+            m_mediaSeeking->GetPositions(&current, &stop);
+
+            const bool shouldSeek = current == stop;
+
+            if (time == -1) {
+                HRESULT hr = m_mediaSeeking->GetDuration(&time);
+                if (FAILED(hr)) {
+                    return;
+                }
+            } else {
+                time *= 10000;
+            }
+
+            if (time == stop) {
+                //the stop position is already at the right place
+                return;
+            }
+
+            if (shouldSeek) {
+                m_mediaSeeking->SetPositions(&current, AM_SEEKING_AbsolutePositioning, 
+                    &time, AM_SEEKING_AbsolutePositioning);
+            } else {
+                m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning, 
+                    &time, AM_SEEKING_AbsolutePositioning);
+            }
+        }
+
+        qint64 MediaGraph::stopPosition() const
+        {
+            qint64 ret;
+            m_mediaSeeking->GetStopPosition(&ret);
+            return ret / 10000;
+
+        }
+
+        QList<qint64> MediaGraph::titles() const
+        {
+            //for now we only manage that for the audio cd
+            ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface);
+            if (titleIFace) {
+                return titleIFace->titles();
+            } else {
+                // the default value: only one title that starts at position 0
+                return QList<qint64>() << 0;
+            }
+        }
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+
+
+    }
+}
+
+QT_END_NAMESPACE