1
/*  This file is part of the KDE project.
2
3
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
5
This library is free software: you can redistribute it and/or modify
6
it under the terms of the GNU Lesser General Public License as published by
7
the Free Software Foundation, either version 2.1 or 3 of the License.
8
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU Lesser General Public License for more details.
13
14
You should have received a copy of the GNU Lesser General Public License
15
along 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
32
QT_BEGIN_NAMESPACE
33
34
namespace 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
            const QList<OutputPin> demuxOutputs = BackendNode::pins(m_demux, PINDIR_OUTPUT);
785
            for (int i = 0; i < demuxOutputs.count(); ++i) {
786
                //...and the output must be decoded
787
                QAMMediaType type;
788
                hr = demuxOutputs.at(i)->ConnectionMediaType(&type);
789
                if (FAILED(hr)) {
790
                    continue;
791
                }
792
793
                if (type.majortype == MEDIATYPE_Video) {
794
                    m_hasVideo = true;
795
                } else if (type.majortype == MEDIATYPE_Audio) {
796
                    m_hasAudio = true;
797
                }
798
            }
799
800
801
            for (int i = 0; i < m_decoders.count(); ++i) {
802
                QList<Filter> chain = getFilterChain(m_demux, m_decoders.at(i));
803
                for (int i = 0; i < chain.count(); ++i) {
804
                    //we keep those filters
805
                    removedFilters.removeOne(chain.at(i));
806
                }
807
            }
808
809
            for (int i = 0; i < removedFilters.count(); ++i) {
810
                graph->RemoveFilter(removedFilters.at(i));
811
            }
812
813
            m_mediaObject->workerThread()->replaceGraphForEventManagement(graph, oldGraph);
814
815
            //let's transfer the nodes from the current graph to the new one
816
            QList<GraphConnection> connections; //we store the connections that need to be restored
817
818
            // First get all the sink nodes (nodes with no input connected)
819
            for (int i = 0; i < m_sinkConnections.count(); ++i) {
820
                Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
821
                connections += getConnections(currentFilter);
822
                grabFilter(currentFilter);
823
            }
824
825
            //we need to do something smart to detect if the streams are unencoded
826
            for (int i = 0; i < demuxOutputs.count(); ++i) {
827
                const OutputPin &out = demuxOutputs.at(i);
828
                InputPin pin;
829
                if (out->ConnectedTo(pin.pparam()) == HRESULT(VFW_E_NOT_CONNECTED)) {
830
                    m_decoderPins += out; //unconnected outputs can be decoded outputs
831
                }
832
            }
833
834
            ensureSourceConnectedTo(true);
835
836
            //let's reestablish the connections
837
            for (int i = 0; i < connections.count(); ++i) {
838
                const GraphConnection &connection = connections.at(i);
839
                //check if we shoud transfer the sink node
840
841
                grabFilter(connection.input);
842
                grabFilter(connection.output);
843
844
                const OutputPin output = BackendNode::pins(connection.output, PINDIR_OUTPUT).at(connection.outputOffset);
845
                const InputPin input   = BackendNode::pins(connection.input, PINDIR_INPUT).at(connection.inputOffset);
846
                HRESULT hr = output->Connect(input, 0);
847
                Q_UNUSED(hr);
848
                Q_ASSERT( SUCCEEDED(hr));
849
            }
850
851
            //Finally, let's update the interfaces
852
            m_mediaControl = ComPointer<IMediaControl>(graph, IID_IMediaControl);
853
            m_mediaSeeking = ComPointer<IMediaSeeking>(graph, IID_IMediaSeeking);
854
            return hr;
855
        }
856
857
        //utility functions
858
        //retrieves the filters between source and sink
859
        QList<Filter> MediaGraph::getFilterChain(const Filter &source, const Filter &sink)
860
        {
861
            QList<Filter> ret;
862
            Filter current = sink;
863
            while (current && BackendNode::pins(current, PINDIR_INPUT).count() == 1 && current != source) {
864
                if (current != source)
865
                    ret += current;
866
                InputPin pin = BackendNode::pins(current, PINDIR_INPUT).first();
867
                current = Filter();
868
                OutputPin output;
869
                if (pin->ConnectedTo(output.pparam()) == S_OK) {
870
                    PIN_INFO info;
871
                    if (SUCCEEDED(output->QueryPinInfo(&info)) && info.pFilter) {
872
                        current = Filter(info.pFilter); //this will take care of releasing the interface pFilter
873
                    }
874
                }
875
            }
876
            if (current != source) {
877
                //the soruce and sink don't seem to be connected
878
                ret.clear();
879
            }
880
            return ret;
881
        }
882
883
        bool MediaGraph::isDecoderFilter(const Filter &filter)
884
        {
885
            if (filter == 0) {
886
                return false;
887
            }
888
#ifdef GRAPH_DEBUG
889
            {
890
                FILTER_INFO info;
891
                filter->QueryFilterInfo(&info);
892
                qDebug() << Q_FUNC_INFO << QString((const QChar *)info.achName);
893
                if (info.pGraph) {
894
                    info.pGraph->Release();
895
                }
896
            }
897
#endif
898
899
900
            QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
901
            QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
902
903
            //TODO: find a better way to detect if a node is a decoder
904
            if (inputs.count() == 0 || outputs.count() ==0) {
905
                return false;
906
            }
907
908
            //the input pin must be encoded data
909
            QAMMediaType type;
910
            HRESULT hr = inputs.first()->ConnectionMediaType(&type);
911
            if (FAILED(hr)) {
912
                return false;
913
            }
914
915
916
            //...and the output must be decoded
917
            QAMMediaType type2;
918
            hr = outputs.first()->ConnectionMediaType(&type2);
919
            if (FAILED(hr)) {
920
                return false;
921
            }
922
923
            if (type2.majortype != MEDIATYPE_Video &&
924
                type2.majortype != MEDIATYPE_Audio) {
925
                    return false;
926
            }
927
928
            if (type2.majortype == MEDIATYPE_Video) {
929
                m_hasVideo = true;
930
            } else {
931
                m_hasAudio = true;
932
            }
933
934
#ifdef GRAPH_DEBUG
935
            {
936
                FILTER_INFO info;
937
                filter->QueryFilterInfo(&info);
938
                qDebug() << "found a decoder filter" << QString((const QChar *)info.achName);
939
                if (info.pGraph) {
940
                    info.pGraph->Release();
941
                }
942
            }
943
#endif
944
945
            return true;
946
        }
947
948
        bool MediaGraph::isSourceFilter(const Filter &filter) const
949
        {
950
#ifdef GRAPH_DEBUG
951
            {
952
                FILTER_INFO info;
953
                filter->QueryFilterInfo(&info);
954
                qDebug() << Q_FUNC_INFO << QString((const QChar *)info.achName);
955
                if (info.pGraph) {
956
                    info.pGraph->Release();
957
                }
958
            }
959
#endif
960
            //a source filter is one that has no input
961
            return BackendNode::pins(filter, PINDIR_INPUT).isEmpty();
962
        }
963
964
        bool MediaGraph::isDemuxerFilter(const Filter &filter) const
965
        {
966
            QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
967
            QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
968
969
#ifdef GRAPH_DEBUG
970
            {
971
                FILTER_INFO info;
972
                filter->QueryFilterInfo(&info);
973
                qDebug() << Q_FUNC_INFO << QString((const QChar *)info.achName);
974
                if (info.pGraph) {
975
                    info.pGraph->Release();
976
                }
977
            }
978
#endif
979
980
            if (inputs.count() != 1 || outputs.count() == 0) {
981
                return false; //a demuxer has only one input
982
            }
983
984
            QAMMediaType type;
985
            HRESULT hr = inputs.first()->ConnectionMediaType(&type);
986
            if (FAILED(hr)) {
987
                return false;
988
            }
989
990
            if (type.majortype != MEDIATYPE_Stream) {
991
                return false;
992
            }
993
994
            for (int i = 0; i < outputs.count(); ++i) {
995
                QAMMediaType type;
996
                //for now we support only video and audio
997
                hr = outputs.at(i)->ConnectionMediaType(&type);
998
                if (SUCCEEDED(hr) && 
999
                    type.majortype != MEDIATYPE_Video && type.majortype != MEDIATYPE_Audio) {
1000
                        return false;
1001
                }
1002
            }
1003
#ifdef GRAPH_DEBUG
1004
            {
1005
                FILTER_INFO info;
1006
                filter->QueryFilterInfo(&info);
1007
                qDebug() << "found a demuxer filter" << QString((const QChar *)info.achName);
1008
                if (info.pGraph) {
1009
                    info.pGraph->Release();
1010
                }
1011
            }
1012
#endif
1013
            return true;
1014
        }
1015
1016
        QMultiMap<QString, QString> MediaGraph::metadata() const
1017
        {
1018
            QMultiMap<QString, QString> ret;
1019
            ComPointer<IAMMediaContent> mediaContent(m_demux, IID_IAMMediaContent);
1020
            if (mediaContent) {
1021
                //let's get the meta data
1022
                BSTR str;
1023
                HRESULT hr = mediaContent->get_AuthorName(&str);
1024
                if (SUCCEEDED(hr)) {
1025
                    ret.insert(QLatin1String("ARTIST"), QString::fromWCharArray(str));
1026
                    SysFreeString(str);
1027
                }
1028
                hr = mediaContent->get_Title(&str);
1029
                if (SUCCEEDED(hr)) {
1030
                    ret.insert(QLatin1String("TITLE"), QString::fromWCharArray(str));
1031
                    SysFreeString(str);
1032
                }
1033
                hr = mediaContent->get_Description(&str);
1034
                if (SUCCEEDED(hr)) {
1035
                    ret.insert(QLatin1String("DESCRIPTION"), QString::fromWCharArray(str));
1036
                    SysFreeString(str);
1037
                }
1038
                hr = mediaContent->get_Copyright(&str);
1039
                if (SUCCEEDED(hr)) {
1040
                    ret.insert(QLatin1String("COPYRIGHT"), QString::fromWCharArray(str));
1041
                    SysFreeString(str);
1042
                }
1043
                hr = mediaContent->get_MoreInfoText(&str);
1044
                if (SUCCEEDED(hr)) {
1045
                    ret.insert(QLatin1String("MOREINFO"), QString::fromWCharArray(str));
1046
                    SysFreeString(str);
1047
                }
1048
            }
1049
            return ret;
1050
        }
1051
1052
        Filter MediaGraph::realSource() const
1053
        {
1054
            return m_realSource;
1055
        }
1056
1057
#ifndef QT_NO_PHONON_MEDIACONTROLLER
1058
        void MediaGraph::setStopPosition(qint64 time)
1059
        {
1060
            qint64 current = 0,
1061
                stop = 0;
1062
            m_mediaSeeking->GetPositions(&current, &stop);
1063
1064
            const bool shouldSeek = current == stop;
1065
1066
            if (time == -1) {
1067
                HRESULT hr = m_mediaSeeking->GetDuration(&time);
1068
                if (FAILED(hr)) {
1069
                    return;
1070
                }
1071
            } else {
1072
                time *= 10000;
1073
            }
1074
1075
            if (time == stop) {
1076
                //the stop position is already at the right place
1077
                return;
1078
            }
1079
1080
            if (shouldSeek) {
1081
                m_mediaSeeking->SetPositions(&current, AM_SEEKING_AbsolutePositioning, 
1082
                    &time, AM_SEEKING_AbsolutePositioning);
1083
            } else {
1084
                m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning, 
1085
                    &time, AM_SEEKING_AbsolutePositioning);
1086
            }
1087
        }
1088
1089
        qint64 MediaGraph::stopPosition() const
1090
        {
1091
            qint64 ret;
1092
            m_mediaSeeking->GetStopPosition(&ret);
1093
            return ret / 10000;
1094
1095
        }
1096
1097
        QList<qint64> MediaGraph::titles() const
1098
        {
1099
            //for now we only manage that for the audio cd
1100
            ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface);
1101
            if (titleIFace) {
1102
                return titleIFace->titles();
1103
            } else {
1104
                // the default value: only one title that starts at position 0
1105
                return QList<qint64>() << 0;
1106
            }
1107
        }
1108
#endif //QT_NO_PHONON_MEDIACONTROLLER
1109
1110
1111
1112
    }
1113
}
1114
1115
QT_END_NAMESPACE