source: trunk/src/3rdparty/phonon/ds9/mediagraph.cpp@ 138

Last change on this file since 138 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 37.9 KB
Line 
1/* This file is part of the KDE project.
2
3Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
5This library is free software: you can redistribute it and/or modify
6it under the terms of the GNU Lesser General Public License as published by
7the Free Software Foundation, either version 2.1 or 3 of the License.
8
9This library is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU Lesser General Public License for more details.
13
14You should have received a copy of the GNU Lesser General Public License
15along with this library. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "fakesource.h"
19#include "iodevicereader.h"
20#include "qaudiocdreader.h"
21
22#include "mediagraph.h"
23#include "mediaobject.h"
24
25
26#include <QtCore/QUrl>
27#include <QtCore/QDebug>
28
29#include <qnetwork.h>
30
31
32QT_BEGIN_NAMESPACE
33
34namespace Phonon
35{
36 namespace DS9
37 {
38 //description of a connection
39 struct GraphConnection
40 {
41 Filter output;
42 int outputOffset;
43 Filter input;
44 int inputOffset;
45 };
46
47 static QList<GraphConnection> getConnections(Filter source)
48 {
49 QList<GraphConnection> ret;
50 int outOffset = 0;
51 const QList<OutputPin> outputs = BackendNode::pins(source, PINDIR_OUTPUT);
52 for (int i = 0; i < outputs.count(); ++i) {
53 InputPin input;
54 if (outputs.at(i)->ConnectedTo(input.pparam()) == S_OK) {
55 PIN_INFO info;
56 input->QueryPinInfo(&info);
57 Filter current(info.pFilter);
58 if (current) {
59 //this is a valid connection
60 const int inOffset = BackendNode::pins(current, PINDIR_INPUT).indexOf(input);
61 const GraphConnection connection = {source, outOffset, current, inOffset};
62 ret += connection;
63 ret += getConnections(current); //get subsequent connections
64 }
65 }
66 outOffset++;
67 }
68 return ret;
69 }
70
71 static HRESULT saveToFile(Graph graph, const QString &filepath)
72 {
73 const WCHAR wszStreamName[] = L"ActiveMovieGraph";
74 HRESULT hr;
75 ComPointer<IStorage> storage;
76
77 // First, create a document file that will hold the GRF file
78 hr = StgCreateDocfile((OLECHAR*)filepath.utf16(),
79 STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE |
80 STGM_SHARE_EXCLUSIVE,
81 0, storage.pparam());
82
83 if (FAILED(hr)) {
84 return hr;
85 }
86
87 // Next, create a stream to store.
88 ComPointer<IStream> stream;
89 hr = storage->CreateStream(wszStreamName,
90 STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
91 0, 0, stream.pparam());
92
93 if (FAILED(hr)) {
94 return hr;
95 }
96
97 // The IpersistStream::Save method converts a stream into a persistent object.
98 ComPointer<IPersistStream> persist(graph, IID_IPersistStream);
99 hr = persist->Save(stream, TRUE);
100 if (SUCCEEDED(hr)) {
101 hr = storage->Commit(STGC_DEFAULT);
102 }
103
104 return hr;
105 }
106
107
108 MediaGraph::MediaGraph(MediaObject *mo, short index) :
109 m_graph(CLSID_FilterGraph, IID_IGraphBuilder),
110 m_fakeSource(new FakeSource()),
111 m_hasVideo(false), m_hasAudio(false), m_connectionsDirty(false),
112 m_isStopping(false), m_isSeekable(false), m_result(S_OK),
113 m_index(index), m_renderId(0), m_seekId(0),
114 m_currentTime(0), m_totalTime(0), m_mediaObject(mo)
115 {
116 m_mediaControl = ComPointer<IMediaControl>(m_graph, IID_IMediaControl);
117 Q_ASSERT(m_mediaControl);
118 m_mediaSeeking = ComPointer<IMediaSeeking>(m_graph, IID_IMediaSeeking);
119 Q_ASSERT(m_mediaSeeking);
120
121 HRESULT hr = m_graph->AddFilter(m_fakeSource, 0);
122 if (m_mediaObject->catchComError(hr)) {
123 return;
124 }
125 }
126
127 MediaGraph::~MediaGraph()
128 {
129 }
130
131 short MediaGraph::index() const
132 {
133 return m_index;
134 }
135
136 void MediaGraph::grabNode(BackendNode *node)
137 {
138 grabFilter(node->filter(m_index));
139 }
140
141 void MediaGraph::grabFilter(Filter filter)
142 {
143 if (filter) {
144 FILTER_INFO info;
145 filter->QueryFilterInfo(&info);
146 if (info.pGraph != m_graph) {
147 if (info.pGraph) {
148 m_mediaObject->catchComError(info.pGraph->RemoveFilter(filter));
149 }
150 m_mediaObject->catchComError(m_graph->AddFilter(filter, 0));
151 }
152 if (info.pGraph) {
153 info.pGraph->Release();
154 }
155 }
156 }
157
158 void MediaGraph::switchFilters(Filter oldFilter, Filter newFilter)
159 {
160 OAFilterState state = syncGetRealState();
161 if (state != State_Stopped) {
162 ensureStopped(); //to do the transaction
163 }
164
165
166 OutputPin connected;
167 {
168 InputPin pin = BackendNode::pins(oldFilter, PINDIR_INPUT).first();
169 pin->ConnectedTo(connected.pparam());
170 }
171
172 m_graph->RemoveFilter(oldFilter);
173 m_graph->AddFilter(newFilter, 0);
174
175 if (connected) {
176 InputPin pin = BackendNode::pins(newFilter, PINDIR_INPUT).first();
177 //let's reestablish the connections
178 m_graph->Connect(connected, pin);
179 }
180
181 switch(state)
182 {
183 case State_Running:
184 play();
185 break;
186 case State_Paused:
187 pause();
188 break;
189 default:
190 break;
191 }
192
193 }
194
195 OAFilterState MediaGraph::syncGetRealState() const
196 {
197 OAFilterState state;
198 m_mediaControl->GetState(INFINITE, &state);
199 return state;
200 }
201
202
203
204 void MediaGraph::ensureSourceDisconnected()
205 {
206 for (int i = 0; i < m_sinkConnections.count(); ++i) {
207 const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
208 const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
209 const QList<InputPin> outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
210
211 for (int i = 0; i < inputs.count(); ++i) {
212 for (int o = 0; o < outputs.count(); o++) {
213 tryDisconnect(outputs.at(o), inputs.at(i));
214 }
215
216 for (int d = 0; d < m_decoderPins.count(); ++d) {
217 tryDisconnect(m_decoderPins.at(d), inputs.at(i));
218 }
219 }
220 }
221 }
222
223 void MediaGraph::ensureSourceConnectedTo(bool force)
224 {
225 if (m_connectionsDirty == false && force == false) {
226 return;
227 }
228
229 m_connectionsDirty = false;
230 ensureSourceDisconnected();
231
232 //reconnect the pins
233 for (int i = 0; i < m_sinkConnections.count(); ++i) {
234 const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
235 const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
236 for(int i = 0; i < inputs.count(); ++i) {
237 //we ensure the filter belongs to the graph
238 grabFilter(currentFilter);
239
240 for (int d = 0; d < m_decoderPins.count(); ++d) {
241 //a decoder has only one output
242 if (tryConnect(m_decoderPins.at(d), inputs.at(i))) {
243 break;
244 }
245 }
246 }
247 }
248 }
249
250 QList<Filter> MediaGraph::getAllFilters(Graph graph)
251 {
252 QList<Filter> ret;
253 ComPointer<IEnumFilters> enumFilters;
254 graph->EnumFilters(enumFilters.pparam());
255 Filter current;
256 while( enumFilters && enumFilters->Next(1, current.pparam(), 0) == S_OK) {
257 ret += current;
258 }
259 return ret;
260 }
261
262 QList<Filter> MediaGraph::getAllFilters() const
263 {
264 return getAllFilters(m_graph);
265 }
266
267
268 bool MediaGraph::isSeekable() const
269 {
270 return m_isSeekable;
271 }
272
273 qint64 MediaGraph::absoluteTotalTime() const
274 {
275 if (m_seekId) {
276 return m_totalTime;
277 } else {
278 qint64 ret = 0;
279 if (m_mediaSeeking) {
280 m_mediaSeeking->GetDuration(&ret);
281 ret /= 10000; //convert to milliseconds
282 }
283 return ret;
284 }
285 }
286
287 qint64 MediaGraph::absoluteCurrentTime() const
288 {
289 if (m_seekId) {
290 return m_currentTime;
291 } else {
292 qint64 ret = -1;
293 if (m_mediaSeeking) {
294 HRESULT hr = m_mediaSeeking->GetCurrentPosition(&ret);
295 if (FAILED(hr)) {
296 return ret;
297 }
298 ret /= 10000; //convert to milliseconds
299 }
300 return ret;
301 }
302 }
303
304 Phonon::MediaSource MediaGraph::mediaSource() const
305 {
306 return m_mediaSource;
307 }
308
309 void MediaGraph::play()
310 {
311 ensureSourceConnectedTo();
312 m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Running, m_decoders);
313 }
314
315 void MediaGraph::pause()
316 {
317 ensureSourceConnectedTo();
318 m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Paused, m_decoders);
319 }
320
321 HRESULT MediaGraph::renderResult() const
322 {
323 return m_result;
324 }
325
326 bool MediaGraph::isStopping() const
327 {
328 return m_isStopping;
329 }
330
331 Graph MediaGraph::graph() const
332 {
333 return m_graph;
334 }
335
336 void MediaGraph::stop()
337 {
338 if (!isLoading()) {
339 ensureStopped();
340 absoluteSeek(0); //resets the clock
341 } else {
342 m_mediaObject->workerThread()->abortCurrentRender(m_renderId);
343 m_renderId = 0; //cancels current loading
344 }
345 m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Stopped);
346 }
347
348 void MediaGraph::ensureStopped()
349 {
350 m_isStopping = true;
351 //special case here because we want stopped to be synchronous
352 m_graph->Abort();
353 m_mediaControl->Stop();
354 OAFilterState dummy;
355 //this will wait until the change is effective
356 m_mediaControl->GetState(INFINITE, &dummy);
357 m_isStopping = false;
358 }
359
360 bool MediaGraph::isLoading() const
361 {
362 return m_renderId != 0;
363 }
364
365 void MediaGraph::absoluteSeek(qint64 time)
366 {
367 //this just sends a request
368 if (m_seekId == 0) {
369 m_currentTime = absoluteCurrentTime();
370 m_totalTime = absoluteTotalTime();
371 }
372 m_seekId = m_mediaObject->workerThread()->addSeekRequest(m_graph, time);
373 }
374
375 HRESULT MediaGraph::removeFilter(const Filter& filter)
376 {
377 FILTER_INFO info;
378 filter->QueryFilterInfo(&info);
379#ifdef GRAPH_DEBUG
380 qDebug() << "removeFilter" << QString::fromUtf16(info.achName);
381#endif
382 if (info.pGraph) {
383 info.pGraph->Release();
384 return m_graph->RemoveFilter(filter);
385 }
386
387 //already removed
388 return S_OK;
389 }
390
391 HRESULT MediaGraph::cleanup()
392 {
393 stop();
394
395 ensureSourceDisconnected();
396
397 QList<Filter> list = m_decoders;
398 if (m_demux) {
399 list << m_demux;
400 }
401 if (m_realSource) {
402 list << m_realSource;
403 }
404 list << m_decoders;
405
406 for (int i = 0; i < m_decoders.count(); ++i) {
407 list += getFilterChain(m_demux, m_decoders.at(i));
408 }
409
410 for (int i = 0; i < list.count(); ++i) {
411 removeFilter(list.at(i));
412 }
413
414 //Let's reinitialize the internal lists
415 m_decoderPins.clear();
416 m_decoders.clear();
417 m_demux = Filter();
418 m_realSource = Filter();
419 m_mediaSource = Phonon::MediaSource();
420
421 absoluteSeek(0); //resets the clock
422
423 return S_OK;
424 }
425
426
427 bool MediaGraph::disconnectNodes(BackendNode *source, BackendNode *sink)
428 {
429 const Filter sinkFilter = sink->filter(m_index);
430 const QList<InputPin> inputs = BackendNode::pins(sinkFilter, PINDIR_INPUT);
431
432 QList<OutputPin> outputs;
433 if (source == m_mediaObject) {
434 outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
435 outputs += m_decoderPins;
436 } else {
437 outputs = BackendNode::pins(source->filter(m_index), PINDIR_OUTPUT);
438 }
439
440
441 for (int i = 0; i < inputs.count(); ++i) {
442 for (int o = 0; o < outputs.count(); ++o) {
443 tryDisconnect(outputs.at(o), inputs.at(i));
444 }
445 }
446
447 if (m_sinkConnections.removeOne(sink)) {
448 m_connectionsDirty = true;
449 }
450 return true;
451 }
452
453 bool MediaGraph::tryDisconnect(const OutputPin &out, const InputPin &in)
454 {
455 bool ret = false;
456
457 OutputPin output;
458 if (SUCCEEDED(in->ConnectedTo(output.pparam()))) {
459
460 if (output == out) {
461 //we need a simple disconnection
462 ret = SUCCEEDED(out->Disconnect()) && SUCCEEDED(in->Disconnect());
463 } else {
464 InputPin in2;
465 if (SUCCEEDED(out->ConnectedTo(in2.pparam()))) {
466 PIN_INFO info;
467 in2->QueryPinInfo(&info);
468 Filter tee(info.pFilter);
469 CLSID clsid;
470 tee->GetClassID(&clsid);
471 if (clsid == CLSID_InfTee) {
472 //we have to remove all intermediate filters between the tee and the sink
473 PIN_INFO info;
474 in->QueryPinInfo(&info);
475 Filter sink(info.pFilter);
476 QList<Filter> list = getFilterChain(tee, sink);
477 out->QueryPinInfo(&info);
478 Filter source(info.pFilter);
479
480 if (list.isEmpty()) {
481 output->QueryPinInfo(&info);
482 if (Filter(info.pFilter) == tee) {
483 ret = SUCCEEDED(output->Disconnect()) && SUCCEEDED(in->Disconnect());
484 }
485 } else {
486 ret = true;
487 for (int i = 0; i < list.count(); ++i) {
488 ret = ret && SUCCEEDED(removeFilter(list.at(i)));
489 }
490 }
491
492 //Let's try to see if the Tee filter is still useful
493 if (ret) {
494 int connections = 0;
495 const QList<OutputPin> outputs = BackendNode::pins(tee, PINDIR_OUTPUT);
496 for(int i = 0; i < outputs.count(); ++i) {
497 InputPin p;
498 if ( SUCCEEDED(outputs.at(i)->ConnectedTo(p.pparam()))) {
499 connections++;
500 }
501 }
502 if (connections == 0) {
503 //this avoids a crash if the filter is destroyed
504 //by the subsequent call to removeFilter
505 output = OutputPin();
506 removeFilter(tee); //there is no more output for the tee, we remove it
507 }
508 }
509 }
510 }
511 }
512 }
513 return ret;
514 }
515
516 bool MediaGraph::tryConnect(const OutputPin &out, const InputPin &newIn)
517 {
518
519
520 ///The management of the creation of the Tees is done here (this is the only place where we call IPin::Connect
521 InputPin inPin;
522 if (SUCCEEDED(out->ConnectedTo(inPin.pparam()))) {
523
524 //the fake source has another mechanism for the connection
525 if (BackendNode::pins(m_fakeSource, PINDIR_OUTPUT).contains(out)) {
526 return false;
527 }
528
529 //the output pin is already connected
530 PIN_INFO info;
531 inPin->QueryPinInfo(&info);
532 Filter filter(info.pFilter); //this will ensure the interface is "Release"d
533 CLSID clsid;
534 filter->GetClassID(&clsid);
535 if (clsid == CLSID_InfTee) {
536 //there is already a Tee (namely 'filter') in use
537 const QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
538 for(int i = 0; i < outputs.count(); ++i) {
539 const OutputPin &pin = outputs.at(i);
540 if (VFW_E_NOT_CONNECTED == pin->ConnectedTo(inPin.pparam())) {
541 return SUCCEEDED(pin->Connect(newIn, 0));
542 }
543 }
544 //we shoud never go here
545 return false;
546 } else {
547 QAMMediaType type;
548 out->ConnectionMediaType(&type);
549
550 //first we disconnect the current connection (and we save the current media type)
551 if (!tryDisconnect(out, inPin)) {
552 return false;
553 }
554
555 //..then we try to connect the new node
556 if (SUCCEEDED(out->Connect(newIn, 0))) {
557
558 //we have to insert the Tee
559 if (!tryDisconnect(out, newIn)) {
560 return false;
561 }
562
563 Filter filter(CLSID_InfTee, IID_IBaseFilter);
564 if (!filter) {
565 //rollback
566 m_graph->Connect(out, inPin);
567 return false;
568 }
569
570 if (FAILED(m_graph->AddFilter(filter, 0))) {
571 return false;
572 }
573
574
575 InputPin teeIn = BackendNode::pins(filter, PINDIR_INPUT).first(); //a Tee has always one input
576 HRESULT hr = out->Connect(teeIn, &type);
577 if (FAILED(hr)) {
578 hr = m_graph->Connect(out, teeIn);
579 }
580 if (FAILED(hr)) {
581 m_graph->Connect(out, inPin);
582 return false;
583 }
584
585 OutputPin teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
586
587 //we simply reconnect the pins as they
588 hr = m_graph->Connect(teeOut, inPin);
589 if (FAILED(hr)) {
590 m_graph->Connect(out, inPin);
591 return false;
592 }
593
594 teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
595 if (FAILED(m_graph->Connect(teeOut, newIn))) {
596 m_graph->Connect(out, inPin);
597 return false;
598 }
599
600 return true;
601 } else {
602 //we simply reconnect the pins as they
603 m_graph->Connect(out, inPin);
604 return false;
605 }
606 }
607
608 } else {
609 return SUCCEEDED(m_graph->Connect(out, newIn));
610 }
611 }
612
613 bool MediaGraph::connectNodes(BackendNode *source, BackendNode *sink)
614 {
615 bool ret = false;
616 const QList<InputPin> inputs = BackendNode::pins(sink->filter(m_index), PINDIR_INPUT);
617 QList<OutputPin> outputs = BackendNode::pins(source == m_mediaObject ? m_fakeSource : source->filter(m_index), PINDIR_OUTPUT);
618
619 if (source == m_mediaObject) {
620 grabFilter(m_fakeSource);
621 }
622
623#ifdef GRAPH_DEBUG
624 qDebug() << Q_FUNC_INFO << source << sink << this;
625#endif
626
627 for (int o = 0; o < outputs.count(); o++) {
628 InputPin p;
629 for (int i = 0; i < inputs.count(); i++) {
630 const InputPin &inPin = inputs.at(i);
631 if (tryConnect(outputs.at(o), inPin)) {
632 //tell the sink node that it just got a new input
633 sink->connected(source, inPin);
634 ret = true;
635 if (source == m_mediaObject) {
636 m_connectionsDirty = true;
637 m_sinkConnections += sink;
638#ifdef GRAPH_DEBUG
639 qDebug() << "found a sink connection" << sink << m_sinkConnections.count();
640#endif
641 }
642 break;
643 }
644 }
645 }
646
647 return ret;
648 }
649
650
651 HRESULT MediaGraph::loadSource(const Phonon::MediaSource &source)
652 {
653 m_hasVideo = false;
654 m_hasAudio = false;
655 m_isSeekable = false;
656
657
658 //cleanup of the previous filters
659 m_result = cleanup();
660 if (FAILED(m_result)) {
661 return m_result;
662 }
663
664 m_mediaSource = source;
665
666 switch (source.type())
667 {
668 case Phonon::MediaSource::Disc:
669 if (source.discType() == Phonon::Dvd) {
670 m_result = E_NOTIMPL;
671 /*m_realSource = Filter(CLSID_DVDNavigator, IID_IBaseFilter);
672 if (m_realSource) {
673 return REGDB_E_CLASSNOTREG;
674 }
675
676 m_result = m_graph->AddFilter(m_realSource, L"DVD Navigator");*/
677
678
679 #ifndef QT_NO_PHONON_MEDIACONTROLLER
680 } else if (source.discType() == Phonon::Cd) {
681 m_realSource = Filter(new QAudioCDPlayer);
682 m_result = m_graph->AddFilter(m_realSource, 0);
683
684#endif //QT_NO_PHONON_MEDIACONTROLLER
685 } else {
686 m_result = E_NOTIMPL;
687 }
688 if (FAILED(m_result)) {
689 return m_result;
690 }
691 m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
692 return m_result;
693 case Phonon::MediaSource::Invalid:
694 return m_result;
695 case Phonon::MediaSource::Url:
696 case Phonon::MediaSource::LocalFile:
697 {
698 QString url;
699 if (source.type() == Phonon::MediaSource::LocalFile) {
700 url = source.fileName();
701 } else {
702 url = source.url().toString();
703 }
704 m_renderId = m_mediaObject->workerThread()->addUrlToRender(url);
705 }
706 break;
707#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
708 case Phonon::MediaSource::Stream:
709 {
710 m_realSource = Filter(new IODeviceReader(source, this));
711 m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
712 }
713 break;
714#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
715 default:
716 m_result = E_FAIL;
717 }
718
719 return m_result;
720 }
721
722 void MediaGraph::finishSeeking(quint16 workId, qint64 time)
723 {
724 if (m_seekId == workId) {
725 m_currentTime = time;
726 m_mediaObject->seekingFinished(this);
727 m_seekId = 0;
728 } else {
729 //it's a queue seek command
730 //we're still seeking
731 }
732 }
733
734 void MediaGraph::finishLoading(quint16 workId, HRESULT hr, Graph graph)
735 {
736 if (m_renderId == workId) {
737 m_renderId = 0;
738
739 //let's determine if the graph is seekable
740 {
741 ComPointer<IMediaSeeking> mediaSeeking(graph, IID_IMediaSeeking);
742 DWORD caps = AM_SEEKING_CanSeekAbsolute;
743 m_isSeekable = mediaSeeking && SUCCEEDED(mediaSeeking->CheckCapabilities(&caps));
744 }
745
746 m_result = reallyFinishLoading(hr, graph);
747 m_mediaObject->loadingFinished(this);
748 }
749 }
750
751
752 HRESULT MediaGraph::reallyFinishLoading(HRESULT hr, const Graph &graph)
753 {
754 if (FAILED(hr)) {
755 return hr;
756 }
757
758 const Graph oldGraph = m_graph;
759 m_graph = graph;
760
761 //we keep the source and all the way down to the decoders
762 QList<Filter> removedFilters;
763
764 const QList<Filter> allFilters = getAllFilters(graph);
765 for (int i = 0; i < allFilters.count(); ++i) {
766 const Filter &filter = allFilters.at(i);
767 if (isSourceFilter(filter)) {
768 m_realSource = filter; //save the source filter
769 if (!m_demux ) {
770 m_demux = filter; //in the WMV case, the demuxer is the source filter itself
771 }
772 } else if (isDemuxerFilter(filter)) {
773 m_demux = filter;
774 } else if (isDecoderFilter(filter)) {
775 m_decoders += filter;
776 m_decoderPins += BackendNode::pins(filter, PINDIR_OUTPUT).first();
777 } else {
778 removedFilters += filter;
779 }
780 }
781
782 for (int i = 0; i < m_decoders.count(); ++i) {
783 QList<Filter> chain = getFilterChain(m_demux, m_decoders.at(i));
784 for (int i = 0; i < chain.count(); ++i) {
785 //we keep those filters
786 removedFilters.removeOne(chain.at(i));
787 }
788 }
789
790 for (int i = 0; i < removedFilters.count(); ++i) {
791 graph->RemoveFilter(removedFilters.at(i));
792 }
793
794 m_mediaObject->workerThread()->replaceGraphForEventManagement(graph, oldGraph);
795
796 //let's transfer the nodes from the current graph to the new one
797 QList<GraphConnection> connections; //we store the connections that need to be restored
798
799 // First get all the sink nodes (nodes with no input connected)
800 for (int i = 0; i < m_sinkConnections.count(); ++i) {
801 Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
802 connections += getConnections(currentFilter);
803 grabFilter(currentFilter);
804 }
805
806 //we need to do something smart to detect if the streams are unencoded
807 if (m_demux) {
808 const QList<OutputPin> outputs = BackendNode::pins(m_demux, PINDIR_OUTPUT);
809 for (int i = 0; i < outputs.count(); ++i) {
810 const OutputPin &out = outputs.at(i);
811 InputPin pin;
812 if (out->ConnectedTo(pin.pparam()) == VFW_E_NOT_CONNECTED) {
813 m_decoderPins += out; //unconnected outputs can be decoded outputs
814 }
815 }
816 }
817
818 ensureSourceConnectedTo(true);
819
820 //let's reestablish the connections
821 for (int i = 0; i < connections.count(); ++i) {
822 const GraphConnection &connection = connections.at(i);
823 //check if we shoud transfer the sink node
824
825 grabFilter(connection.input);
826 grabFilter(connection.output);
827
828 const OutputPin output = BackendNode::pins(connection.output, PINDIR_OUTPUT).at(connection.outputOffset);
829 const InputPin input = BackendNode::pins(connection.input, PINDIR_INPUT).at(connection.inputOffset);
830 HRESULT hr = output->Connect(input, 0);
831 Q_UNUSED(hr);
832 Q_ASSERT( SUCCEEDED(hr));
833 }
834
835 //Finally, let's update the interfaces
836 m_mediaControl = ComPointer<IMediaControl>(graph, IID_IMediaControl);
837 m_mediaSeeking = ComPointer<IMediaSeeking>(graph, IID_IMediaSeeking);
838 return hr;
839 }
840
841 //utility functions
842 //retrieves the filters between source and sink
843 QList<Filter> MediaGraph::getFilterChain(const Filter &source, const Filter &sink)
844 {
845 QList<Filter> ret;
846 Filter current = sink;
847 while (current && BackendNode::pins(current, PINDIR_INPUT).count() == 1 && current != source) {
848 if (current != source)
849 ret += current;
850 InputPin pin = BackendNode::pins(current, PINDIR_INPUT).first();
851 current = Filter();
852 OutputPin output;
853 if (pin->ConnectedTo(output.pparam()) == S_OK) {
854 PIN_INFO info;
855 if (SUCCEEDED(output->QueryPinInfo(&info)) && info.pFilter) {
856 current = Filter(info.pFilter); //this will take care of releasing the interface pFilter
857 }
858 }
859 }
860 if (current != source) {
861 //the soruce and sink don't seem to be connected
862 ret.clear();
863 }
864 return ret;
865 }
866
867 bool MediaGraph::isDecoderFilter(const Filter &filter)
868 {
869 if (filter == 0) {
870 return false;
871 }
872#ifdef GRAPH_DEBUG
873 {
874 FILTER_INFO info;
875 filter->QueryFilterInfo(&info);
876 qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
877 if (info.pGraph) {
878 info.pGraph->Release();
879 }
880 }
881#endif
882
883
884 QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
885 QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
886
887 //TODO: find a better way to detect if a node is a decoder
888 if (inputs.count() == 0 || outputs.count() ==0) {
889 return false;
890 }
891
892 //the input pin must be encoded data
893 QAMMediaType type;
894 HRESULT hr = inputs.first()->ConnectionMediaType(&type);
895 if (FAILED(hr)) {
896 return false;
897 }
898
899
900 //...and the output must be decoded
901 QAMMediaType type2;
902 hr = outputs.first()->ConnectionMediaType(&type2);
903 if (FAILED(hr)) {
904 return false;
905 }
906
907 if (type2.majortype != MEDIATYPE_Video &&
908 type2.majortype != MEDIATYPE_Audio) {
909 return false;
910 }
911
912 if (type2.majortype == MEDIATYPE_Video) {
913 m_hasVideo = true;
914 } else {
915 m_hasAudio = true;
916 }
917
918#ifdef GRAPH_DEBUG
919 {
920 FILTER_INFO info;
921 filter->QueryFilterInfo(&info);
922 qDebug() << "found a decoder filter" << QString::fromUtf16(info.achName);
923 if (info.pGraph) {
924 info.pGraph->Release();
925 }
926 }
927#endif
928
929 return true;
930 }
931
932 bool MediaGraph::isSourceFilter(const Filter &filter) const
933 {
934#ifdef GRAPH_DEBUG
935 {
936 FILTER_INFO info;
937 filter->QueryFilterInfo(&info);
938 qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
939 if (info.pGraph) {
940 info.pGraph->Release();
941 }
942 }
943#endif
944 //a source filter is one that has no input
945 return BackendNode::pins(filter, PINDIR_INPUT).isEmpty();
946 }
947
948 bool MediaGraph::isDemuxerFilter(const Filter &filter) const
949 {
950 QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
951 QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
952
953#ifdef GRAPH_DEBUG
954 {
955 FILTER_INFO info;
956 filter->QueryFilterInfo(&info);
957 qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName);
958 if (info.pGraph) {
959 info.pGraph->Release();
960 }
961 }
962#endif
963
964 if (inputs.count() != 1 || outputs.count() == 0) {
965 return false; //a demuxer has only one input
966 }
967
968 QAMMediaType type;
969 HRESULT hr = inputs.first()->ConnectionMediaType(&type);
970 if (FAILED(hr)) {
971 return false;
972 }
973
974 if (type.majortype != MEDIATYPE_Stream) {
975 return false;
976 }
977
978 for (int i = 0; i < outputs.count(); ++i) {
979 QAMMediaType type;
980 //for now we support only video and audio
981 hr = outputs.at(i)->ConnectionMediaType(&type);
982 if (SUCCEEDED(hr) &&
983 type.majortype != MEDIATYPE_Video && type.majortype != MEDIATYPE_Audio) {
984 return false;
985 }
986 }
987#ifdef GRAPH_DEBUG
988 {
989 FILTER_INFO info;
990 filter->QueryFilterInfo(&info);
991 qDebug() << "found a demuxer filter" << QString::fromUtf16(info.achName);
992 if (info.pGraph) {
993 info.pGraph->Release();
994 }
995 }
996#endif
997 return true;
998 }
999
1000 QMultiMap<QString, QString> MediaGraph::metadata() const
1001 {
1002 QMultiMap<QString, QString> ret;
1003 ComPointer<IAMMediaContent> mediaContent(m_demux, IID_IAMMediaContent);
1004 if (mediaContent) {
1005 //let's get the meta data
1006 BSTR str;
1007 HRESULT hr = mediaContent->get_AuthorName(&str);
1008 if (SUCCEEDED(hr)) {
1009 ret.insert(QLatin1String("ARTIST"), QString::fromUtf16((const unsigned short*)str));
1010 SysFreeString(str);
1011 }
1012 hr = mediaContent->get_Title(&str);
1013 if (SUCCEEDED(hr)) {
1014 ret.insert(QLatin1String("TITLE"), QString::fromUtf16((const unsigned short*)str));
1015 SysFreeString(str);
1016 }
1017 hr = mediaContent->get_Description(&str);
1018 if (SUCCEEDED(hr)) {
1019 ret.insert(QLatin1String("DESCRIPTION"), QString::fromUtf16((const unsigned short*)str));
1020 SysFreeString(str);
1021 }
1022 hr = mediaContent->get_Copyright(&str);
1023 if (SUCCEEDED(hr)) {
1024 ret.insert(QLatin1String("COPYRIGHT"), QString::fromUtf16((const unsigned short*)str));
1025 SysFreeString(str);
1026 }
1027 hr = mediaContent->get_MoreInfoText(&str);
1028 if (SUCCEEDED(hr)) {
1029 ret.insert(QLatin1String("MOREINFO"), QString::fromUtf16((const unsigned short*)str));
1030 SysFreeString(str);
1031 }
1032 }
1033 return ret;
1034 }
1035
1036 Filter MediaGraph::realSource() const
1037 {
1038 return m_realSource;
1039 }
1040
1041#ifndef QT_NO_PHONON_MEDIACONTROLLER
1042 void MediaGraph::setStopPosition(qint64 time)
1043 {
1044 qint64 current = 0,
1045 stop = 0;
1046 m_mediaSeeking->GetPositions(&current, &stop);
1047
1048 const bool shouldSeek = current == stop;
1049
1050 if (time == -1) {
1051 HRESULT hr = m_mediaSeeking->GetDuration(&time);
1052 if (FAILED(hr)) {
1053 return;
1054 }
1055 } else {
1056 time *= 10000;
1057 }
1058
1059 if (time == stop) {
1060 //the stop position is already at the right place
1061 return;
1062 }
1063
1064 if (shouldSeek) {
1065 m_mediaSeeking->SetPositions(&current, AM_SEEKING_AbsolutePositioning,
1066 &time, AM_SEEKING_AbsolutePositioning);
1067 } else {
1068 m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning,
1069 &time, AM_SEEKING_AbsolutePositioning);
1070 }
1071 }
1072
1073 qint64 MediaGraph::stopPosition() const
1074 {
1075 qint64 ret;
1076 m_mediaSeeking->GetStopPosition(&ret);
1077 return ret / 10000;
1078
1079 }
1080
1081 QList<qint64> MediaGraph::titles() const
1082 {
1083 //for now we only manage that for the audio cd
1084 ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface);
1085 if (titleIFace) {
1086 return titleIFace->titles();
1087 } else {
1088 // the default value: only one title that starts at position 0
1089 return QList<qint64>() << 0;
1090 }
1091 }