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

Last change on this file was 846, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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