diff options
Diffstat (limited to 'src/3rdparty/phonon/ds9/mediagraph.cpp')
-rw-r--r-- | src/3rdparty/phonon/ds9/mediagraph.cpp | 1099 |
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(¤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 |