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 "qbasefilter.h"
19
#include "qpin.h"
20
#include "compointer.h"
21
22
#include <QtCore/QMutex>
23
24
QT_BEGIN_NAMESPACE
25
26
namespace Phonon
27
{
28
    namespace DS9
29
    {
30
31
        static const AM_MEDIA_TYPE defaultMediaType = { MEDIATYPE_NULL, MEDIASUBTYPE_NULL, TRUE, FALSE, 1, GUID_NULL, 0, 0, 0};
32
33
        class QEnumMediaTypes : public IEnumMediaTypes
34
        {
35
        public:
36
            QEnumMediaTypes(QPin *pin) :  m_refCount(1), m_pin(pin), m_index(0)
37
            {
38
                m_pin->AddRef();
39
            }
40
41
            ~QEnumMediaTypes()
42
            {
43
                m_pin->Release();
44
            }
45
46
            STDMETHODIMP QueryInterface(const IID &iid,void **out)
47
            {
48
                if (!out) {
49
                    return E_POINTER;
50
                }
51
52
                HRESULT hr = S_OK;
53
                if (iid == IID_IEnumMediaTypes) {
54
                    *out = static_cast<IEnumMediaTypes*>(this);
55
                } else if (iid == IID_IUnknown) {
56
                    *out = static_cast<IUnknown*>(this);
57
                } else {
58
                    *out = 0;
59
                    hr = E_NOINTERFACE;
60
                }
61
62
                if (hr == S_OK) {
63
                    AddRef();
64
                }
65
                return hr;
66
            }
67
68
            STDMETHODIMP_(ULONG) AddRef()
69
            {
70
                return InterlockedIncrement(&m_refCount);
71
            }
72
73
            STDMETHODIMP_(ULONG) Release()
74
            {
75
                ULONG refCount = InterlockedDecrement(&m_refCount);
76
                if (refCount == 0) {
77
                    delete this;
78
                }
79
80
                return refCount;
81
            }
82
83
            STDMETHODIMP Next(ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched)
84
            {
85
                QMutexLocker locker(&m_mutex);
86
                if (!out) {
87
                    return E_POINTER;
88
                }
89
90
                if (!fetched && count > 1) {
91
                    return E_INVALIDARG;
92
                }
93
94
                uint nbFetched = 0;
95
                while (nbFetched < count && m_index < m_pin->mediaTypes().count()) {
96
                    //the caller will deallocate the memory
97
                    *out = static_cast<AM_MEDIA_TYPE *>(::CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)));
98
                    const AM_MEDIA_TYPE original = m_pin->mediaTypes().at(m_index);
99
                    **out = QPin::copyMediaType(original);
100
                    nbFetched++;
101
                    m_index++;
102
                    out++;
103
                }
104
105
                if (fetched) {
106
                    *fetched = nbFetched;
107
                }
108
109
                return nbFetched == count ? S_OK : S_FALSE;
110
            }
111
112
            STDMETHODIMP Skip(ULONG count)
113
            {
114
                QMutexLocker locker(&m_mutex);
115
                m_index = qMin(m_index + int(count), m_pin->mediaTypes().count());
116
                return  (m_index == m_pin->mediaTypes().count()) ? S_FALSE : S_OK;
117
            }
118
119
            STDMETHODIMP Reset()
120
            {
121
                QMutexLocker locker(&m_mutex);
122
                m_index = 0;
123
                return S_OK;
124
            }
125
126
            STDMETHODIMP Clone(IEnumMediaTypes **out)
127
            {
128
                QMutexLocker locker(&m_mutex);
129
                if (!out) {
130
                    return E_POINTER;
131
                }
132
133
                *out = new QEnumMediaTypes(m_pin);
134
                (*out)->Skip(m_index);
135
                return S_OK;
136
            }
137
138
139
        private:
140
            LONG m_refCount;
141
            QPin *m_pin;
142
            int m_index;
143
            QMutex m_mutex;
144
        };
145
146
147
        QPin::QPin(QBaseFilter *parent, PIN_DIRECTION dir, const QVector<AM_MEDIA_TYPE> &mt) :
148
            m_parent(parent), m_flushing(false), m_refCount(1),  m_connected(0),
149
            m_direction(dir), m_mediaTypes(mt), m_connectedType(defaultMediaType),
150
            m_memAlloc(0)
151
        {
152
            Q_ASSERT(m_parent);
153
            m_parent->addPin(this);
154
        }
155
156
        QPin::~QPin()
157
        {
158
            m_parent->removePin(this);
159
            setMemoryAllocator(0);
160
            freeMediaType(m_connectedType);
161
        }
162
163
        //reimplementation from IUnknown
164
        STDMETHODIMP QPin::QueryInterface(REFIID iid, void**out)
165
        {
166
            if (!out) {
167
                return E_POINTER;
168
            }
169
170
            HRESULT hr = S_OK;
171
172
            if (iid == IID_IPin) {
173
                *out = static_cast<IPin*>(this);
174
            } else if (iid == IID_IUnknown) {
175
                *out = static_cast<IUnknown*>(this);
176
            } else if (m_direction == PINDIR_OUTPUT && (iid == IID_IMediaSeeking || iid == IID_IMediaPosition)) {
177
                return m_parent->QueryInterface(iid, out);
178
            } else {
179
                *out = 0;
180
                hr = E_NOINTERFACE;
181
            }
182
183
            if (hr == S_OK) {
184
                AddRef();
185
            }
186
            return hr;
187
        }
188
189
        STDMETHODIMP_(ULONG) QPin::AddRef()
190
        {
191
            return InterlockedIncrement(&m_refCount);
192
        }
193
194
        STDMETHODIMP_(ULONG) QPin::Release()
195
        {
196
            ULONG refCount = InterlockedDecrement(&m_refCount);
197
            if (refCount == 0) {
198
                delete this;
199
            }
200
201
            return refCount;
202
        }
203
204
        //this is called on the input pins
205
        STDMETHODIMP QPin::ReceiveConnection(IPin *pin, const AM_MEDIA_TYPE *type)
206
        {
207
            if (!pin ||!type) {
208
                return E_POINTER;
209
            }
210
211
            if (connected()) {
212
                return VFW_E_ALREADY_CONNECTED;
213
            }
214
215
            if (filterState() != State_Stopped) {
216
                return VFW_E_NOT_STOPPED;
217
            }
218
219
            if (QueryAccept(type) != S_OK) {
220
                return VFW_E_TYPE_NOT_ACCEPTED;
221
            }
222
223
            setConnected(pin);
224
            setConnectedType(*type);
225
226
            return S_OK;
227
        }
228
229
        //this is called on the output pins
230
        STDMETHODIMP QPin::Connect(IPin *pin, const AM_MEDIA_TYPE *type)
231
        {
232
            if (!pin) {
233
                return E_POINTER;
234
            }
235
236
            if (connected()) {
237
                return VFW_E_ALREADY_CONNECTED;
238
            }
239
240
            if (filterState() != State_Stopped) {
241
                return VFW_E_NOT_STOPPED;
242
            }
243
244
            HRESULT hr = S_OK;
245
246
            setConnected(pin);
247
            if (!type) {
248
249
                //let(s first try the output pin's mediaTypes
250
                if (checkOutputMediaTypesConnection(pin) != S_OK &&
251
                    checkOwnMediaTypesConnection(pin) != S_OK) {
252
                        hr = VFW_E_NO_ACCEPTABLE_TYPES;
253
                }
254
            } else if (QueryAccept(type) == S_OK) {
255
                setConnectedType(*type);
256
                hr = pin->ReceiveConnection(this, type);
257
            } else {
258
                hr = VFW_E_TYPE_NOT_ACCEPTED;
259
            }
260
261
            if (FAILED(hr)) {
262
                setConnected(0);
263
                setConnectedType(defaultMediaType);
264
            } else {
265
                ComPointer<IMemInputPin> input(pin, IID_IMemInputPin);
266
                if (input) {
267
                    ComPointer<IMemAllocator> alloc;
268
                    input->GetAllocator(alloc.pparam());
269
                    if (alloc) {
270
                        //be default we take the allocator from the input pin
271
                        //we have no reason to force using our own
272
                        setMemoryAllocator(alloc);
273
                    }
274
                }
275
                if (memoryAllocator() == 0) {
276
                    ALLOCATOR_PROPERTIES prop;
277
                    if (input && input->GetAllocatorRequirements(&prop) == S_OK) {
278
                        createDefaultMemoryAllocator(&prop);
279
                    } else {
280
                        createDefaultMemoryAllocator();
281
                    }
282
                }
283
284
                Q_ASSERT(memoryAllocator() != 0);
285
                if (input) {
286
                    input->NotifyAllocator(memoryAllocator(), TRUE); //TRUE is arbitrarily chosen here
287
                }
288
289
            }
290
291
            return hr;
292
        }
293
294
        STDMETHODIMP QPin::Disconnect()
295
        {
296
            if (!connected()) {
297
                return S_FALSE;
298
            }
299
300
            if (filterState() != State_Stopped) {
301
                return VFW_E_NOT_STOPPED;
302
            }
303
304
            setConnected(0);
305
            setConnectedType(defaultMediaType);
306
            setMemoryAllocator(0);
307
            return S_OK;
308
        }
309
310
        STDMETHODIMP QPin::ConnectedTo(IPin **other)
311
        {
312
            if (!other) {
313
                return E_POINTER;
314
            }
315
316
            *other = connected(true);
317
            if (!(*other)) {
318
                return VFW_E_NOT_CONNECTED;
319
            }
320
321
            return S_OK;
322
        }
323
324
        STDMETHODIMP QPin::ConnectionMediaType(AM_MEDIA_TYPE *type)
325
        {
326
            QMutexLocker locker(&m_mutex);
327
            if (!type) {
328
                return E_POINTER;
329
            }
330
331
            *type = copyMediaType(m_connectedType);
332
            if (!m_connected) {
333
                return VFW_E_NOT_CONNECTED;
334
            } else {
335
                return S_OK;
336
            }
337
        }
338
339
        STDMETHODIMP QPin::QueryPinInfo(PIN_INFO *info)
340
        {
341
            if (!info) {
342
                return E_POINTER;
343
            }
344
345
            info->dir = m_direction;
346
            info->pFilter = m_parent;
347
            m_parent->AddRef();
348
            info->achName[0] = 0;
349
            return S_OK;
350
        }
351
352
        STDMETHODIMP QPin::QueryDirection(PIN_DIRECTION *dir)
353
        {
354
            if (!dir) {
355
                return E_POINTER;
356
            }
357
358
            *dir = m_direction;
359
            return S_OK;
360
        }
361
362
        STDMETHODIMP QPin::QueryId(LPWSTR *id)
363
        {
364
            if (!id) {
365
                return E_POINTER;
366
            }
367
368
            *id = static_cast<LPWSTR>(::CoTaskMemAlloc(2));
369
            *id[0] = 0;
370
            return S_OK;
371
        }
372
373
        STDMETHODIMP QPin::QueryAccept(const AM_MEDIA_TYPE *type)
374
        {
375
            QMutexLocker locker(&m_mutex);
376
            if (!type) {
377
                return E_POINTER;
378
            }
379
380
            for (int i = 0; i < m_mediaTypes.count(); ++i) {
381
                const AM_MEDIA_TYPE &current = m_mediaTypes.at(i);
382
                if ( (type->majortype == current.majortype) &&
383
                    (current.subtype == MEDIASUBTYPE_NULL || type->subtype == current.subtype) &&
384
                    (type->majortype == MEDIATYPE_Stream || type->formattype != GUID_NULL || current.formattype != GUID_NULL) &&
385
                    (current.formattype == GUID_NULL || type->formattype == current.formattype)
386
                    ) {
387
                        return S_OK;
388
                }
389
            }
390
            return S_FALSE;
391
        }
392
393
394
        STDMETHODIMP QPin::EnumMediaTypes(IEnumMediaTypes **emt)
395
        {
396
            if (!emt) {
397
                return E_POINTER;
398
            }
399
400
            *emt = new QEnumMediaTypes(this);
401
            return S_OK;
402
        }
403
404
405
        STDMETHODIMP QPin::EndOfStream()
406
        {
407
            return E_UNEXPECTED;
408
        }
409
410
        STDMETHODIMP QPin::BeginFlush()
411
        {
412
            return E_UNEXPECTED;
413
        }
414
415
        STDMETHODIMP QPin::EndFlush()
416
        {
417
            return E_UNEXPECTED;
418
        }
419
420
        STDMETHODIMP QPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
421
        {
422
            QMutexLocker locker(&m_mutex);
423
            if (m_direction == PINDIR_OUTPUT && m_connected) {
424
                //we deliver this downstream
425
                m_connected->NewSegment(start, stop, rate);
426
            }
427
            return S_OK;
428
        }
429
430
        STDMETHODIMP QPin::QueryInternalConnections(IPin **, ULONG*)
431
        {
432
            //this is not implemented on purpose (the input pins are connected to all the output pins)
433
            return E_NOTIMPL;
434
        }
435
436
437
        HRESULT QPin::checkOutputMediaTypesConnection(IPin *pin)
438
        {
439
            ComPointer<IEnumMediaTypes> emt;
440
            HRESULT hr = pin->EnumMediaTypes(emt.pparam());
441
            if (hr != S_OK) {
442
                return hr;
443
            }
444
445
            AM_MEDIA_TYPE *type = 0;
446
            while (emt->Next(1, &type, 0) == S_OK) {
447
                if (QueryAccept(type) == S_OK) {
448
                    setConnectedType(*type);
449
                    if (pin->ReceiveConnection(this, type) == S_OK) {
450
                        freeMediaType(type);
451
                        return S_OK;
452
                    } else {
453
                        setConnectedType(defaultMediaType);
454
                        freeMediaType(type);
455
                    }
456
                }
457
            }
458
459
            //we didn't find a suitable type
460
            return S_FALSE;
461
        }
462
463
        HRESULT QPin::checkOwnMediaTypesConnection(IPin *pin)
464
        {   
465
            for(int i = 0; i < m_mediaTypes.count(); ++i) {
466
                const AM_MEDIA_TYPE &current = m_mediaTypes.at(i);
467
                setConnectedType(current);
468
                HRESULT hr = pin->ReceiveConnection(this, &current);
469
                if (hr == S_OK) {
470
                    return S_OK;
471
                }
472
            }
473
474
            //we didn't find a suitable type
475
            return S_FALSE;
476
        }
477
478
        void QPin::freeMediaType(const AM_MEDIA_TYPE &type)
479
        {
480
            if (type.cbFormat) {
481
                ::CoTaskMemFree(type.pbFormat);
482
            }
483
            if (type.pUnk) {
484
                type.pUnk->Release();
485
            }
486
        }
487
488
        void QPin::freeMediaType(AM_MEDIA_TYPE *type)
489
        {
490
            freeMediaType(*type);
491
            ::CoTaskMemFree(type);
492
        }
493
494
        //addition
495
496
        PIN_DIRECTION QPin::direction() const
497
        {
498
            return m_direction;
499
        }
500
501
        void QPin::setConnectedType(const AM_MEDIA_TYPE &type)
502
        {
503
            QMutexLocker locker(&m_mutex);
504
505
            //1st we free memory
506
            freeMediaType(m_connectedType);
507
508
            m_connectedType = copyMediaType(type);
509
        }
510
511
        const AM_MEDIA_TYPE &QPin::connectedType() const 
512
        {
513
            QMutexLocker locker(&m_mutex);
514
            return m_connectedType;
515
        }
516
517
        void QPin::setConnected(IPin *pin)
518
        {
519
            QMutexLocker locker(&m_mutex);
520
            if (pin) {
521
                pin->AddRef();
522
            }
523
            if (m_connected) {
524
                m_connected->Release();
525
            }
526
            m_connected = pin;
527
        }
528
529
        IPin *QPin::connected(bool addref) const
530
        {
531
            QMutexLocker locker(&m_mutex);
532
            if (addref && m_connected) {
533
                m_connected->AddRef();
534
            }
535
            return m_connected;
536
        }
537
538
        bool QPin::isFlushing() const
539
        {
540
            QMutexLocker locker(&m_mutex);
541
            return m_flushing;
542
        }
543
544
        FILTER_STATE QPin::filterState() const
545
        {
546
            FILTER_STATE fstate = State_Stopped;
547
            m_parent->GetState(0, &fstate);
548
            return fstate;
549
        }
550
551
        QVector<AM_MEDIA_TYPE> QPin::mediaTypes() const
552
        {
553
            QMutexLocker locker(&m_mutex);
554
            return m_mediaTypes;
555
        }
556
557
        HRESULT QPin::setAcceptedMediaType(const AM_MEDIA_TYPE &mt)
558
        {
559
            const QVector<AM_MEDIA_TYPE> oldMediaTypes = m_mediaTypes;
560
            m_mediaTypes = QVector<AM_MEDIA_TYPE>() << mt;
561
562
            HRESULT hr = S_OK;
563
564
            IPin *conn = connected();
565
            if (conn) {
566
                //try to reconnect to redefine the media type
567
                conn->Disconnect();
568
                Disconnect();
569
                hr = Connect(conn, 0);
570
                if (FAILED(hr)) {
571
                    m_mediaTypes = oldMediaTypes;
572
                    Connect(conn, 0); //just redo the connection with the old media types
573
                }
574
            }
575
            return hr;
576
        }
577
578
        void QPin::createDefaultMemoryAllocator(ALLOCATOR_PROPERTIES *prop)
579
        {
580
            ComPointer<IMemAllocator> alloc(CLSID_MemoryAllocator, IID_IMemAllocator);
581
            if (prop) {
582
                alloc->SetProperties(prop, 0);
583
            }
584
            setMemoryAllocator(alloc);
585
        }
586
587
        void QPin::setMemoryAllocator(IMemAllocator *alloc)
588
        {
589
            QMutexLocker locker(&m_mutex);
590
            if (alloc) {
591
                alloc->AddRef();
592
            }
593
            if (m_memAlloc) {
594
                m_memAlloc->Release();
595
            }
596
            m_memAlloc = alloc;
597
        }
598
599
        IMemAllocator *QPin::memoryAllocator(bool addref) const
600
        {
601
            QMutexLocker locker(&m_mutex);
602
            if (addref && m_memAlloc) {
603
                m_memAlloc->AddRef();
604
            }
605
            return m_memAlloc;
606
        }
607
608
        AM_MEDIA_TYPE QPin::copyMediaType(const AM_MEDIA_TYPE &type)
609
        {
610
            AM_MEDIA_TYPE ret = type;
611
612
            //make a deep copy here
613
            if (ret.cbFormat == 0 || ret.pbFormat == 0) {
614
                ret.cbFormat = 0;
615
                ret.pbFormat = 0;
616
                ret.formattype = GUID_NULL;
617
            } else {
618
                ret.pbFormat = reinterpret_cast<BYTE*>(::CoTaskMemAlloc(type.cbFormat));
619
                qMemCopy(ret.pbFormat, type.pbFormat, type.cbFormat);
620
            }
621
622
            if (type.pUnk) {
623
                type.pUnk->AddRef();
624
            }
625
            return ret;
626
        }
627
628
629
    }
630
}
631
632
QT_END_NAMESPACE