summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/phonon/ds9/mediagraph.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/phonon/ds9/mediagraph.cpp')
-rw-r--r--src/3rdparty/phonon/ds9/mediagraph.cpp1099
1 files changed, 1099 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/ds9/mediagraph.cpp b/src/3rdparty/phonon/ds9/mediagraph.cpp
new file mode 100644
index 0000000..31a0622
--- /dev/null
+++ b/src/3rdparty/phonon/ds9/mediagraph.cpp
@@ -0,0 +1,1099 @@
+/* This file is part of the KDE project.
+
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+
+This library is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 2.1 or 3 of the License.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "fakesource.h"
+#include "iodevicereader.h"
+#include "qaudiocdreader.h"
+
+#include "mediagraph.h"
+#include "mediaobject.h"
+
+
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+
+#include <qnetwork.h>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace Phonon
+{
+ namespace DS9
+ {
+ //description of a connection
+ struct GraphConnection
+ {
+ Filter output;
+ int outputOffset;
+ Filter input;
+ int inputOffset;
+ };
+
+ static QList<GraphConnection> getConnections(Filter source)
+ {
+ QList<GraphConnection> ret;
+ int outOffset = 0;
+ const QList<OutputPin> outputs = BackendNode::pins(source, PINDIR_OUTPUT);
+ for (int i = 0; i < outputs.count(); ++i) {
+ InputPin input;
+ if (outputs.at(i)->ConnectedTo(input.pparam()) == S_OK) {
+ PIN_INFO info;
+ input->QueryPinInfo(&info);
+ Filter current(info.pFilter);
+ if (current) {
+ //this is a valid connection
+ const int inOffset = BackendNode::pins(current, PINDIR_INPUT).indexOf(input);
+ const GraphConnection connection = {source, outOffset, current, inOffset};
+ ret += connection;
+ ret += getConnections(current); //get subsequent connections
+ }
+ }
+ outOffset++;
+ }
+ return ret;
+ }
+
+ static HRESULT saveToFile(Graph graph, const QString &filepath)
+ {
+ const WCHAR wszStreamName[] = L"ActiveMovieGraph";
+ HRESULT hr;
+ ComPointer<IStorage> storage;
+
+ // First, create a document file that will hold the GRF file
+ hr = StgCreateDocfile((OLECHAR*)filepath.utf16(),
+ STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE |
+ STGM_SHARE_EXCLUSIVE,
+ 0, storage.pparam());
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Next, create a stream to store.
+ ComPointer<IStream> stream;
+ hr = storage->CreateStream(wszStreamName,
+ STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
+ 0, 0, stream.pparam());
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // The IpersistStream::Save method converts a stream into a persistent object.
+ ComPointer<IPersistStream> persist(graph, IID_IPersistStream);
+ hr = persist->Save(stream, TRUE);
+ if (SUCCEEDED(hr)) {
+ hr = storage->Commit(STGC_DEFAULT);
+ }
+
+ return hr;
+ }
+
+
+ MediaGraph::MediaGraph(MediaObject *mo, short index) :
+ m_graph(CLSID_FilterGraph, IID_IGraphBuilder),
+ m_fakeSource(new FakeSource()),
+ m_hasVideo(false), m_hasAudio(false), m_connectionsDirty(false),
+ m_isStopping(false), m_isSeekable(false), m_result(S_OK),
+ m_index(index), m_renderId(0), m_seekId(0),
+ m_currentTime(0), m_totalTime(0), m_mediaObject(mo)
+ {
+ m_mediaControl = ComPointer<IMediaControl>(m_graph, IID_IMediaControl);
+ Q_ASSERT(m_mediaControl);
+ m_mediaSeeking = ComPointer<IMediaSeeking>(m_graph, IID_IMediaSeeking);
+ Q_ASSERT(m_mediaSeeking);
+
+ HRESULT hr = m_graph->AddFilter(m_fakeSource, 0);
+ if (m_mediaObject->catchComError(hr)) {
+ return;
+ }
+ }
+
+ MediaGraph::~MediaGraph()
+ {
+ }
+
+ short MediaGraph::index() const
+ {
+ return m_index;
+ }
+
+ void MediaGraph::grabNode(BackendNode *node)
+ {
+ grabFilter(node->filter(m_index));
+ }
+
+ void MediaGraph::grabFilter(Filter filter)
+ {
+ if (filter) {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ if (info.pGraph != m_graph) {
+ if (info.pGraph) {
+ m_mediaObject->catchComError(info.pGraph->RemoveFilter(filter));
+ }
+ m_mediaObject->catchComError(m_graph->AddFilter(filter, 0));
+ }
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+ }
+
+ void MediaGraph::switchFilters(Filter oldFilter, Filter newFilter)
+ {
+ OAFilterState state = syncGetRealState();
+ if (state != State_Stopped) {
+ ensureStopped(); //to do the transaction
+ }
+
+
+ OutputPin connected;
+ {
+ InputPin pin = BackendNode::pins(oldFilter, PINDIR_INPUT).first();
+ pin->ConnectedTo(connected.pparam());
+ }
+
+ m_graph->RemoveFilter(oldFilter);
+ m_graph->AddFilter(newFilter, 0);
+
+ if (connected) {
+ InputPin pin = BackendNode::pins(newFilter, PINDIR_INPUT).first();
+ //let's reestablish the connections
+ m_graph->Connect(connected, pin);
+ }
+
+ switch(state)
+ {
+ case State_Running:
+ play();
+ break;
+ case State_Paused:
+ pause();
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ OAFilterState MediaGraph::syncGetRealState() const
+ {
+ OAFilterState state;
+ m_mediaControl->GetState(INFINITE, &state);
+ return state;
+ }
+
+
+
+ void MediaGraph::ensureSourceDisconnected()
+ {
+ for (int i = 0; i < m_sinkConnections.count(); ++i) {
+ const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
+ const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
+ const QList<InputPin> outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
+
+ for (int i = 0; i < inputs.count(); ++i) {
+ for (int o = 0; o < outputs.count(); o++) {
+ tryDisconnect(outputs.at(o), inputs.at(i));
+ }
+
+ for (int d = 0; d < m_decoderPins.count(); ++d) {
+ tryDisconnect(m_decoderPins.at(d), inputs.at(i));
+ }
+ }
+ }
+ }
+
+ void MediaGraph::ensureSourceConnectedTo(bool force)
+ {
+ if (m_connectionsDirty == false && force == false) {
+ return;
+ }
+
+ m_connectionsDirty = false;
+ ensureSourceDisconnected();
+
+ //reconnect the pins
+ for (int i = 0; i < m_sinkConnections.count(); ++i) {
+ const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
+ const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
+ for(int i = 0; i < inputs.count(); ++i) {
+ //we ensure the filter belongs to the graph
+ grabFilter(currentFilter);
+
+ for (int d = 0; d < m_decoderPins.count(); ++d) {
+ //a decoder has only one output
+ if (tryConnect(m_decoderPins.at(d), inputs.at(i))) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ QList<Filter> MediaGraph::getAllFilters(Graph graph)
+ {
+ QList<Filter> ret;
+ ComPointer<IEnumFilters> enumFilters;
+ graph->EnumFilters(enumFilters.pparam());
+ Filter current;
+ while( enumFilters && enumFilters->Next(1, current.pparam(), 0) == S_OK) {
+ ret += current;
+ }
+ return ret;
+ }
+
+ QList<Filter> MediaGraph::getAllFilters() const
+ {
+ return getAllFilters(m_graph);
+ }
+
+
+ bool MediaGraph::isSeekable() const
+ {
+ return m_isSeekable;
+ }
+
+ qint64 MediaGraph::absoluteTotalTime() const
+ {
+ if (m_seekId) {
+ return m_totalTime;
+ } else {
+ qint64 ret = 0;
+ if (m_mediaSeeking) {
+ m_mediaSeeking->GetDuration(&ret);
+ ret /= 10000; //convert to milliseconds
+ }
+ return ret;
+ }
+ }
+
+ qint64 MediaGraph::absoluteCurrentTime() const
+ {
+ if (m_seekId) {
+ return m_currentTime;
+ } else {
+ qint64 ret = -1;
+ if (m_mediaSeeking) {
+ HRESULT hr = m_mediaSeeking->GetCurrentPosition(&ret);
+ if (FAILED(hr)) {
+ return ret;
+ }
+ ret /= 10000; //convert to milliseconds
+ }
+ return ret;
+ }
+ }
+
+ Phonon::MediaSource MediaGraph::mediaSource() const
+ {
+ return m_mediaSource;
+ }
+
+ void MediaGraph::play()
+ {
+ ensureSourceConnectedTo();
+ m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Running, m_decoders);
+ }
+
+ void MediaGraph::pause()
+ {
+ ensureSourceConnectedTo();
+ m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Paused, m_decoders);
+ }
+
+ HRESULT MediaGraph::renderResult() const
+ {
+ return m_result;
+ }
+
+ bool MediaGraph::isStopping() const
+ {
+ return m_isStopping;
+ }
+
+ Graph MediaGraph::graph() const
+ {
+ return m_graph;
+ }
+
+ void MediaGraph::stop()
+ {
+ if (!isLoading()) {
+ ensureStopped();
+ absoluteSeek(0); //resets the clock
+ } else {
+ m_mediaObject->workerThread()->abortCurrentRender(m_renderId);
+ m_renderId = 0; //cancels current loading
+ }
+ m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Stopped);
+ }
+
+ void MediaGraph::ensureStopped()
+ {
+ m_isStopping = true;
+ //special case here because we want stopped to be synchronous
+ m_graph->Abort();
+ m_mediaControl->Stop();
+ OAFilterState dummy;
+ //this will wait until the change is effective
+ m_mediaControl->GetState(INFINITE, &dummy);
+ m_isStopping = false;
+ }
+
+ bool MediaGraph::isLoading() const
+ {
+ return m_renderId != 0;
+ }
+
+ void MediaGraph::absoluteSeek(qint64 time)
+ {
+ //this just sends a request
+ if (m_seekId == 0) {
+ m_currentTime = absoluteCurrentTime();
+ m_totalTime = absoluteTotalTime();
+ }
+ m_seekId = m_mediaObject->workerThread()->addSeekRequest(m_graph, time);
+ }
+
+ HRESULT MediaGraph::removeFilter(const Filter& filter)
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+#ifdef GRAPH_DEBUG
+ qDebug() << "removeFilter" << QString::fromUtf16(info.achName);
+#endif
+ if (info.pGraph) {
+ info.pGraph->Release();
+ return m_graph->RemoveFilter(filter);
+ }
+
+ //already removed
+ return S_OK;
+ }
+
+ HRESULT MediaGraph::cleanup()
+ {
+ stop();
+
+ ensureSourceDisconnected();
+
+ QList<Filter> list = m_decoders;
+ if (m_demux) {
+ list << m_demux;
+ }
+ if (m_realSource) {
+ list << m_realSource;
+ }
+ list << m_decoders;
+
+ for (int i = 0; i < m_decoders.count(); ++i) {
+ list += getFilterChain(m_demux, m_decoders.at(i));
+ }
+
+ for (int i = 0; i < list.count(); ++i) {
+ removeFilter(list.at(i));
+ }
+
+ //Let's reinitialize the internal lists
+ m_decoderPins.clear();
+ m_decoders.clear();
+ m_demux = Filter();
+ m_realSource = Filter();
+ m_mediaSource = Phonon::MediaSource();
+
+ absoluteSeek(0); //resets the clock
+
+ return S_OK;
+ }
+
+
+ bool MediaGraph::disconnectNodes(BackendNode *source, BackendNode *sink)
+ {
+ const Filter sinkFilter = sink->filter(m_index);
+ const QList<InputPin> inputs = BackendNode::pins(sinkFilter, PINDIR_INPUT);
+
+ QList<OutputPin> outputs;
+ if (source == m_mediaObject) {
+ outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
+ outputs += m_decoderPins;
+ } else {
+ outputs = BackendNode::pins(source->filter(m_index), PINDIR_OUTPUT);
+ }
+
+
+ for (int i = 0; i < inputs.count(); ++i) {
+ for (int o = 0; o < outputs.count(); ++o) {
+ tryDisconnect(outputs.at(o), inputs.at(i));
+ }
+ }
+
+ if (m_sinkConnections.removeOne(sink)) {
+ m_connectionsDirty = true;
+ }
+ return true;
+ }
+
+ bool MediaGraph::tryDisconnect(const OutputPin &out, const InputPin &in)
+ {
+ bool ret = false;
+
+ OutputPin output;
+ if (SUCCEEDED(in->ConnectedTo(output.pparam()))) {
+
+ if (output == out) {
+ //we need a simple disconnection
+ ret = SUCCEEDED(out->Disconnect()) && SUCCEEDED(in->Disconnect());
+ } else {
+ InputPin in2;
+ if (SUCCEEDED(out->ConnectedTo(in2.pparam()))) {
+ PIN_INFO info;
+ in2->QueryPinInfo(&info);
+ Filter tee(info.pFilter);
+ CLSID clsid;
+ tee->GetClassID(&clsid);
+ if (clsid == CLSID_InfTee) {
+ //we have to remove all intermediate filters between the tee and the sink
+ PIN_INFO info;
+ in->QueryPinInfo(&info);
+ Filter sink(info.pFilter);
+ QList<Filter> list = getFilterChain(tee, sink);
+ out->QueryPinInfo(&info);
+ Filter source(info.pFilter);
+
+ if (list.isEmpty()) {
+ output->QueryPinInfo(&info);
+ if (Filter(info.pFilter) == tee) {
+ ret = SUCCEEDED(output->Disconnect()) && SUCCEEDED(in->Disconnect());
+ }
+ } else {
+ ret = true;
+ for (int i = 0; i < list.count(); ++i) {
+ ret = ret && SUCCEEDED(removeFilter(list.at(i)));
+ }
+ }
+
+ //Let's try to see if the Tee filter is still useful
+ if (ret) {
+ int connections = 0;
+ const QList<OutputPin> outputs = BackendNode::pins(tee, PINDIR_OUTPUT);
+ for(int i = 0; i < outputs.count(); ++i) {
+ InputPin p;
+ if ( SUCCEEDED(outputs.at(i)->ConnectedTo(p.pparam()))) {
+ connections++;
+ }
+ }
+ if (connections == 0) {
+ //this avoids a crash if the filter is destroyed
+ //by the subsequent call to removeFilter
+ output = OutputPin();
+ removeFilter(tee); //there is no more output for the tee, we remove it
+ }
+ }
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
+ bool MediaGraph::tryConnect(const OutputPin &out, const InputPin &newIn)
+ {
+
+
+ ///The management of the creation of the Tees is done here (this is the only place where we call IPin::Connect
+ InputPin inPin;
+ if (SUCCEEDED(out->ConnectedTo(inPin.pparam()))) {
+
+ //the fake source has another mechanism for the connection
+ if (BackendNode::pins(m_fakeSource, PINDIR_OUTPUT).contains(out)) {
+ return false;
+ }
+
+ //the output pin is already connected
+ PIN_INFO info;
+ inPin->QueryPinInfo(&info);
+ Filter filter(info.pFilter); //this will ensure the interface is "Release"d
+ CLSID clsid;
+ filter->GetClassID(&clsid);
+ if (clsid == CLSID_InfTee) {
+ //there is already a Tee (namely 'filter') in use
+ const QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
+ for(int i = 0; i < outputs.count(); ++i) {
+ const OutputPin &pin = outputs.at(i);
+ if (VFW_E_NOT_CONNECTED == pin->ConnectedTo(inPin.pparam())) {
+ return SUCCEEDED(pin->Connect(newIn, 0));
+ }
+ }
+ //we shoud never go here
+ return false;
+ } else {
+ QAMMediaType type;
+ out->ConnectionMediaType(&type);
+
+ //first we disconnect the current connection (and we save the current media type)
+ if (!tryDisconnect(out, inPin)) {
+ return false;
+ }
+
+ //..then we try to connect the new node
+ if (SUCCEEDED(out->Connect(newIn, 0))) {
+
+ //we have to insert the Tee
+ if (!tryDisconnect(out, newIn)) {
+ return false;
+ }
+
+ Filter filter(CLSID_InfTee, IID_IBaseFilter);
+ if (!filter) {
+ //rollback
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ if (FAILED(m_graph->AddFilter(filter, 0))) {
+ return false;
+ }
+
+
+ InputPin teeIn = BackendNode::pins(filter, PINDIR_INPUT).first(); //a Tee has always one input
+ HRESULT hr = out->Connect(teeIn, &type);
+ if (FAILED(hr)) {
+ hr = m_graph->Connect(out, teeIn);
+ }
+ if (FAILED(hr)) {
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ OutputPin teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
+
+ //we simply reconnect the pins as they
+ hr = m_graph->Connect(teeOut, inPin);
+ if (FAILED(hr)) {
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
+ if (FAILED(m_graph->Connect(teeOut, newIn))) {
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+
+ return true;
+ } else {
+ //we simply reconnect the pins as they
+ m_graph->Connect(out, inPin);
+ return false;
+ }
+ }
+
+ } else {
+ return SUCCEEDED(m_graph->Connect(out, newIn));
+ }
+ }
+
+ bool MediaGraph::connectNodes(BackendNode *source, BackendNode *sink)
+ {
+ bool ret = false;
+ const QList<InputPin> inputs = BackendNode::pins(sink->filter(m_index), PINDIR_INPUT);
+ QList<OutputPin> outputs = BackendNode::pins(source == m_mediaObject ? m_fakeSource : source->filter(m_index), PINDIR_OUTPUT);
+
+ if (source == m_mediaObject) {
+ grabFilter(m_fakeSource);
+ }
+
+#ifdef GRAPH_DEBUG
+ qDebug() << Q_FUNC_INFO << source << sink << this;
+#endif
+
+ for (int o = 0; o < outputs.count(); o++) {
+ InputPin p;
+ for (int i = 0; i < inputs.count(); i++) {
+ const InputPin &inPin = inputs.at(i);
+ if (tryConnect(outputs.at(o), inPin)) {
+ //tell the sink node that it just got a new input
+ sink->connected(source, inPin);
+ ret = true;
+ if (source == m_mediaObject) {
+ m_connectionsDirty = true;
+ m_sinkConnections += sink;
+#ifdef GRAPH_DEBUG
+ qDebug() << "found a sink connection" << sink << m_sinkConnections.count();
+#endif
+ }
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+
+ HRESULT MediaGraph::loadSource(const Phonon::MediaSource &source)
+ {
+ m_hasVideo = false;
+ m_hasAudio = false;
+ m_isSeekable = false;
+
+
+ //cleanup of the previous filters
+ m_result = cleanup();
+ if (FAILED(m_result)) {
+ return m_result;
+ }
+
+ m_mediaSource = source;
+
+ switch (source.type())
+ {
+ case Phonon::MediaSource::Disc:
+ if (source.discType() == Phonon::Dvd) {
+ m_result = E_NOTIMPL;
+ /*m_realSource = Filter(CLSID_DVDNavigator, IID_IBaseFilter);
+ if (m_realSource) {
+ return REGDB_E_CLASSNOTREG;
+ }
+
+ m_result = m_graph->AddFilter(m_realSource, L"DVD Navigator");*/
+
+
+ #ifndef QT_NO_PHONON_MEDIACONTROLLER
+ } else if (source.discType() == Phonon::Cd) {
+ m_realSource = Filter(new QAudioCDPlayer);
+ m_result = m_graph->AddFilter(m_realSource, 0);
+
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+ } else {
+ m_result = E_NOTIMPL;
+ }
+ if (FAILED(m_result)) {
+ return m_result;
+ }
+ m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
+ return m_result;
+ case Phonon::MediaSource::Invalid:
+ return m_result;
+ case Phonon::MediaSource::Url:
+ case Phonon::MediaSource::LocalFile:
+ {
+ QString url;
+ if (source.type() == Phonon::MediaSource::LocalFile) {
+ url = source.fileName();
+ } else {
+ url = source.url().toString();
+ }
+ m_renderId = m_mediaObject->workerThread()->addUrlToRender(url);
+ }
+ break;
+#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ case Phonon::MediaSource::Stream:
+ {
+ m_realSource = Filter(new IODeviceReader(source, this));
+ m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
+ }
+ break;
+#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
+ default:
+ m_result = E_FAIL;
+ }
+
+ return m_result;
+ }
+
+ void MediaGraph::finishSeeking(quint16 workId, qint64 time)
+ {
+ if (m_seekId == workId) {
+ m_currentTime = time;
+ m_mediaObject->seekingFinished(this);
+ m_seekId = 0;
+ } else {
+ //it's a queue seek command
+ //we're still seeking
+ }
+ }
+
+ void MediaGraph::finishLoading(quint16 workId, HRESULT hr, Graph graph)
+ {
+ if (m_renderId == workId) {
+ m_renderId = 0;
+
+ //let's determine if the graph is seekable
+ {
+ ComPointer<IMediaSeeking> mediaSeeking(graph, IID_IMediaSeeking);
+ DWORD caps = AM_SEEKING_CanSeekAbsolute;
+ m_isSeekable = mediaSeeking && SUCCEEDED(mediaSeeking->CheckCapabilities(&caps));
+ }
+
+ m_result = reallyFinishLoading(hr, graph);
+ m_mediaObject->loadingFinished(this);
+ }
+ }
+
+
+ HRESULT MediaGraph::reallyFinishLoading(HRESULT hr, const Graph &graph)
+ {
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ const Graph oldGraph = m_graph;
+ m_graph = graph;
+
+ //we keep the source and all the way down to the decoders
+ QList<Filter> removedFilters;
+
+ const QList<Filter> allFilters = getAllFilters(graph);
+ for (int i = 0; i < allFilters.count(); ++i) {
+ const Filter &filter = allFilters.at(i);
+ if (isSourceFilter(filter)) {
+ m_realSource = filter; //save the source filter
+ if (!m_demux ) {
+ m_demux = filter; //in the WMV case, the demuxer is the source filter itself
+ }
+ } else if (isDemuxerFilter(filter)) {
+ m_demux = filter;
+ } else if (isDecoderFilter(filter)) {
+ m_decoders += filter;
+ m_decoderPins += BackendNode::pins(filter, PINDIR_OUTPUT).first();
+ } else {
+ removedFilters += filter;
+ }
+ }
+
+ for (int i = 0; i < m_decoders.count(); ++i) {
+ QList<Filter> chain = getFilterChain(m_demux, m_decoders.at(i));
+ for (int i = 0; i < chain.count(); ++i) {
+ //we keep those filters
+ removedFilters.removeOne(chain.at(i));
+ }
+ }
+
+ for (int i = 0; i < removedFilters.count(); ++i) {
+ graph->RemoveFilter(removedFilters.at(i));
+ }
+
+ m_mediaObject->workerThread()->replaceGraphForEventManagement(graph, oldGraph);
+
+ //let's transfer the nodes from the current graph to the new one
+ QList<GraphConnection> connections; //we store the connections that need to be restored
+
+ // First get all the sink nodes (nodes with no input connected)
+ for (int i = 0; i < m_sinkConnections.count(); ++i) {
+ Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
+ connections += getConnections(currentFilter);
+ grabFilter(currentFilter);
+ }
+
+ //we need to do something smart to detect if the streams are unencoded
+ if (m_demux) {
+ const QList<OutputPin> outputs = BackendNode::pins(m_demux, PINDIR_OUTPUT);
+ for (int i = 0; i < outputs.count(); ++i) {
+ const OutputPin &out = outputs.at(i);
+ InputPin pin;
+ if (out->ConnectedTo(pin.pparam()) == VFW_E_NOT_CONNECTED) {
+ m_decoderPins += out; //unconnected outputs can be decoded outputs
+ }
+ }
+ }
+
+ ensureSourceConnectedTo(true);
+
+ //let's reestablish the connections
+ for (int i = 0; i < connections.count(); ++i) {
+ const GraphConnection &connection = connections.at(i);
+ //check if we shoud transfer the sink node
+
+ grabFilter(connection.input);
+ grabFilter(connection.output);
+
+ const OutputPin output = BackendNode::pins(connection.output, PINDIR_OUTPUT).at(connection.outputOffset);
+ const InputPin input = BackendNode::pins(connection.input, PINDIR_INPUT).at(connection.inputOffset);
+ HRESULT hr = output->Connect(input, 0);
+ Q_UNUSED(hr);
+ Q_ASSERT( SUCCEEDED(hr));
+ }
+
+ //Finally, let's update the interfaces
+ m_mediaControl = ComPointer<IMediaControl>(graph, IID_IMediaControl);
+ m_mediaSeeking = ComPointer<IMediaSeeking>(graph, IID_IMediaSeeking);
+ return hr;
+ }
+
+ //utility functions
+ //retrieves the filters between source and sink
+ QList<Filter> MediaGraph::getFilterChain(const Filter &source, const Filter &sink)
+ {
+ QList<Filter> ret;
+ Filter current = sink;
+ while (current && BackendNode::pins(current, PINDIR_INPUT).count() == 1 && current != source) {
+ if (current != source)
+ ret += current;
+ InputPin pin = BackendNode::pins(current, PINDIR_INPUT).first();
+ current = Filter();
+ OutputPin output;
+ if (pin->ConnectedTo(output.pparam()) == S_OK) {
+ PIN_INFO info;
+ if (SUCCEEDED(output->QueryPinInfo(&info)) && info.pFilter) {
+ current = Filter(info.pFilter); //this will take care of releasing the interface pFilter
+ }
+ }
+ }
+ if (current != source) {
+ //the soruce and sink don't seem to be connected
+ ret.clear();
+ }
+ return ret;
+ }
+
+ bool MediaGraph::isDecoderFilter(const Filter &filter)
+ {
+ if (filter == 0) {
+ return false;
+ }
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+
+
+ QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
+ QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
+
+ //TODO: find a better way to detect if a node is a decoder
+ if (inputs.count() == 0 || outputs.count() ==0) {
+ return false;
+ }
+
+ //the input pin must be encoded data
+ QAMMediaType type;
+ HRESULT hr = inputs.first()->ConnectionMediaType(&type);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+
+ //...and the output must be decoded
+ QAMMediaType type2;
+ hr = outputs.first()->ConnectionMediaType(&type2);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (type2.majortype != MEDIATYPE_Video &&
+ type2.majortype != MEDIATYPE_Audio) {
+ return false;
+ }
+
+ if (type2.majortype == MEDIATYPE_Video) {
+ m_hasVideo = true;
+ } else {
+ m_hasAudio = true;
+ }
+
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << "found a decoder filter" << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+
+ return true;
+ }
+
+ bool MediaGraph::isSourceFilter(const Filter &filter) const
+ {
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+ //a source filter is one that has no input
+ return BackendNode::pins(filter, PINDIR_INPUT).isEmpty();
+ }
+
+ bool MediaGraph::isDemuxerFilter(const Filter &filter) const
+ {
+ QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
+ QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
+
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+
+ if (inputs.count() != 1 || outputs.count() == 0) {
+ return false; //a demuxer has only one input
+ }
+
+ QAMMediaType type;
+ HRESULT hr = inputs.first()->ConnectionMediaType(&type);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (type.majortype != MEDIATYPE_Stream) {
+ return false;
+ }
+
+ for (int i = 0; i < outputs.count(); ++i) {
+ QAMMediaType type;
+ //for now we support only video and audio
+ hr = outputs.at(i)->ConnectionMediaType(&type);
+ if (SUCCEEDED(hr) &&
+ type.majortype != MEDIATYPE_Video && type.majortype != MEDIATYPE_Audio) {
+ return false;
+ }
+ }
+#ifdef GRAPH_DEBUG
+ {
+ FILTER_INFO info;
+ filter->QueryFilterInfo(&info);
+ qDebug() << "found a demuxer filter" << QString::fromUtf16(info.achName);
+ if (info.pGraph) {
+ info.pGraph->Release();
+ }
+ }
+#endif
+ return true;
+ }
+
+ QMultiMap<QString, QString> MediaGraph::metadata() const
+ {
+ QMultiMap<QString, QString> ret;
+ ComPointer<IAMMediaContent> mediaContent(m_demux, IID_IAMMediaContent);
+ if (mediaContent) {
+ //let's get the meta data
+ BSTR str;
+ HRESULT hr = mediaContent->get_AuthorName(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("ARTIST"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_Title(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("TITLE"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_Description(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("DESCRIPTION"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_Copyright(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("COPYRIGHT"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ hr = mediaContent->get_MoreInfoText(&str);
+ if (SUCCEEDED(hr)) {
+ ret.insert(QLatin1String("MOREINFO"), QString::fromUtf16((const unsigned short*)str));
+ SysFreeString(str);
+ }
+ }
+ return ret;
+ }
+
+ Filter MediaGraph::realSource() const
+ {
+ return m_realSource;
+ }
+
+#ifndef QT_NO_PHONON_MEDIACONTROLLER
+ void MediaGraph::setStopPosition(qint64 time)
+ {
+ qint64 current = 0,
+ stop = 0;
+ m_mediaSeeking->GetPositions(&current, &stop);
+
+ const bool shouldSeek = current == stop;
+
+ if (time == -1) {
+ HRESULT hr = m_mediaSeeking->GetDuration(&time);
+ if (FAILED(hr)) {
+ return;
+ }
+ } else {
+ time *= 10000;
+ }
+
+ if (time == stop) {
+ //the stop position is already at the right place
+ return;
+ }
+
+ if (shouldSeek) {
+ m_mediaSeeking->SetPositions(&current, AM_SEEKING_AbsolutePositioning,
+ &time, AM_SEEKING_AbsolutePositioning);
+ } else {
+ m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning,
+ &time, AM_SEEKING_AbsolutePositioning);
+ }
+ }
+
+ qint64 MediaGraph::stopPosition() const
+ {
+ qint64 ret;
+ m_mediaSeeking->GetStopPosition(&ret);
+ return ret / 10000;
+
+ }
+
+ QList<qint64> MediaGraph::titles() const
+ {
+ //for now we only manage that for the audio cd
+ ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface);
+ if (titleIFace) {
+ return titleIFace->titles();
+ } else {
+ // the default value: only one title that starts at position 0
+ return QList<qint64>() << 0;
+ }
+ }
+#endif //QT_NO_PHONON_MEDIACONTROLLER
+
+
+
+ }
+}
+
+QT_END_NAMESPACE