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 "qmeminputpin.h"
19
#include "qbasefilter.h"
20
#include "compointer.h"
21
22
#include <QtCore/QDebug>
23
24
QT_BEGIN_NAMESPACE
25
26
namespace Phonon
27
{
28
    namespace DS9
29
    {
30
31
        QMemInputPin::QMemInputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt, bool transform, QPin *output) :
32
            QPin(parent, PINDIR_INPUT, mt), m_shouldDuplicateSamples(true), m_transform(transform), m_output(output)
33
        {
34
        }
35
36
        QMemInputPin::~QMemInputPin()
37
        {
38
        }
39
40
        STDMETHODIMP QMemInputPin::QueryInterface(REFIID iid, void **out)
41
        {
42
            if (!out) {
43
                return E_POINTER;
44
            }
45
46
            if (iid == IID_IMemInputPin) {
47
                *out = static_cast<IMemInputPin*>(this);
48
                AddRef();
49
                return S_OK;
50
            } else {
51
                return QPin::QueryInterface(iid, out);
52
            }
53
        }
54
55
        STDMETHODIMP_(ULONG) QMemInputPin::AddRef()
56
        {
57
            return QPin::AddRef();
58
        }
59
60
        STDMETHODIMP_(ULONG) QMemInputPin::Release()
61
        {
62
            return QPin::Release();
63
        }
64
65
        STDMETHODIMP QMemInputPin::EndOfStream()
66
        {
67
            //this allows to serialize with Receive calls
68
            QMutexLocker locker(&m_mutexReceive);
69
            IPin *conn = m_output ? m_output->connected() : 0;
70
            if (conn) {
71
                conn->EndOfStream();
72
            }
73
            return S_OK;
74
        }
75
76
        STDMETHODIMP QMemInputPin::BeginFlush()
77
        {
78
            //pass downstream
79
            IPin *conn = m_output ? m_output->connected() : 0;
80
            if (conn) {
81
                conn->BeginFlush();
82
            }
83
            QMutexLocker locker(&m_mutex);
84
            m_flushing = true;
85
            return S_OK;
86
        }
87
88
        STDMETHODIMP QMemInputPin::EndFlush()
89
        {
90
            //pass downstream
91
            IPin *conn = m_output ? m_output->connected() : 0;
92
            if (conn) {
93
                conn->EndFlush();
94
            }
95
            QMutexLocker locker(&m_mutex);
96
            m_flushing = false;
97
            return S_OK;
98
        }
99
100
        STDMETHODIMP QMemInputPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
101
        {
102
            if (m_output)
103
                m_output->NewSegment(start, stop, rate);
104
            return S_OK;
105
        }
106
107
        //reimplementation to set the type for the output pin
108
        //no need to make a deep copy here
109
        STDMETHODIMP QMemInputPin::ReceiveConnection(IPin *pin ,const AM_MEDIA_TYPE *mt)
110
        {
111
            HRESULT hr = QPin::ReceiveConnection(pin, mt);
112
            if (hr == S_OK &&
113
                mt->majortype != MEDIATYPE_NULL &&
114
                mt->subtype != MEDIASUBTYPE_NULL &&
115
                mt->formattype != GUID_NULL && m_output) {
116
                    //we tell the output pin that it should connect with this type
117
                    hr = m_output->setAcceptedMediaType(connectedType());
118
            }
119
            return hr;
120
        }
121
122
        STDMETHODIMP QMemInputPin::GetAllocator(IMemAllocator **alloc)
123
        {
124
            if (!alloc) {
125
                return E_POINTER;
126
            }
127
128
            *alloc = memoryAllocator(true);
129
            if (*alloc) {
130
                return S_OK;
131
            }
132
133
            return VFW_E_NO_ALLOCATOR;
134
        }
135
136
        STDMETHODIMP QMemInputPin::NotifyAllocator(IMemAllocator *alloc, BOOL readonly)
137
        {
138
            if (!alloc) {
139
                return E_POINTER;
140
            }
141
142
            {
143
                QMutexLocker locker(&m_mutex);
144
                m_shouldDuplicateSamples = m_transform && readonly;
145
            }
146
147
            setMemoryAllocator(alloc);
148
149
            if (m_output) {
150
                ComPointer<IMemInputPin> input(m_output, IID_IMemInputPin);
151
                input->NotifyAllocator(alloc, m_shouldDuplicateSamples);
152
            }
153
154
            return S_OK;
155
        }
156
157
        STDMETHODIMP QMemInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *prop)
158
        {
159
            if (!prop) {
160
                return E_POINTER;
161
            }
162
163
            //we have no particular requirements
164
            return E_NOTIMPL;
165
        }
166
167
        STDMETHODIMP QMemInputPin::Receive(IMediaSample *sample)
168
        {
169
            QMutexLocker locker(&m_mutexReceive);
170
            if (!sample) {
171
                return E_POINTER;
172
            }
173
174
            if (filterState() == State_Stopped) {
175
                return VFW_E_WRONG_STATE;
176
            }
177
178
            if (isFlushing()) {
179
                return S_FALSE; //we are still flushing
180
            }
181
182
            if (!m_shouldDuplicateSamples) {
183
                //we do it just once
184
                HRESULT hr = m_parent->processSample(sample);
185
                if (!SUCCEEDED(hr)) {
186
                    return hr;
187
                }
188
            }
189
190
            if (m_output) {
191
                IMediaSample *outSample = m_shouldDuplicateSamples ?
192
                    duplicateSampleForOutput(sample, m_output->memoryAllocator())
193
                    : sample;
194
195
                if (m_shouldDuplicateSamples) {
196
                    m_parent->processSample(outSample);
197
                }
198
199
                ComPointer<IMemInputPin> input(m_output->connected(), IID_IMemInputPin);
200
                if (input) {
201
                    input->Receive(outSample);
202
                }
203
204
                if (m_shouldDuplicateSamples) {
205
                    outSample->Release();
206
                }
207
            }
208
            return S_OK;
209
        }
210
211
        STDMETHODIMP QMemInputPin::ReceiveMultiple(IMediaSample **samples,long count,long *nbDone)
212
        {
213
            //no need to lock here: there is no access to member data
214
            if (!samples || !nbDone) {
215
                return E_POINTER;
216
            }
217
218
            *nbDone = 0; //initialization
219
            while( *nbDone != count) {
220
                HRESULT hr = Receive(samples[*nbDone]);
221
                if (FAILED(hr)) {
222
                    return hr;
223
                }
224
                (*nbDone)++;
225
            }
226
227
            return S_OK;
228
        }
229
230
        STDMETHODIMP QMemInputPin::ReceiveCanBlock()
231
        {
232
            //we test the output to see if it can block
233
            if (m_output) {
234
                ComPointer<IMemInputPin> meminput(m_output->connected(), IID_IMemInputPin);
235
                if (meminput && meminput->ReceiveCanBlock() != S_FALSE) {
236
                    return S_OK;
237
                }
238
            }
239
            return S_FALSE;
240
        }
241
242
243
        ALLOCATOR_PROPERTIES QMemInputPin::getDefaultAllocatorProperties() const
244
        {
245
            //those values reduce buffering a lot (good for the volume effect)
246
            ALLOCATOR_PROPERTIES prop = {4096, 1, 1, 0};
247
            return prop;
248
        }
249
250
251
        IMediaSample *QMemInputPin::duplicateSampleForOutput(IMediaSample *sample, IMemAllocator *alloc)
252
        {
253
            LONG length = sample->GetActualDataLength();
254
255
            HRESULT hr = alloc->Commit();
256
            if (hr == HRESULT(VFW_E_SIZENOTSET)) {
257
                ALLOCATOR_PROPERTIES prop = getDefaultAllocatorProperties();
258
                prop.cbBuffer = qMax(prop.cbBuffer, length);
259
                ALLOCATOR_PROPERTIES actual;
260
                //we just try to set the properties...
261
                alloc->SetProperties(&prop, &actual);
262
                hr = alloc->Commit();
263
            }
264
265
            Q_ASSERT(SUCCEEDED(hr));
266
267
            IMediaSample *out;
268
            hr = alloc->GetBuffer(&out, 0, 0, AM_GBF_NOTASYNCPOINT);
269
            Q_ASSERT(SUCCEEDED(hr));
270
271
            //let's copy the sample
272
            {
273
                REFERENCE_TIME start, end;
274
                sample->GetTime(&start, &end);
275
                out->SetTime(&start, &end);
276
            }
277
278
            hr = out->SetActualDataLength(length);
279
            Q_ASSERT(SUCCEEDED(hr));
280
            hr = out->SetDiscontinuity(sample->IsDiscontinuity());
281
            Q_ASSERT(SUCCEEDED(hr));
282
283
            {
284
                LONGLONG start, end;
285
                hr = sample->GetMediaTime(&start, &end);
286
                if (hr != HRESULT(VFW_E_MEDIA_TIME_NOT_SET)) {
287
                    hr = out->SetMediaTime(&start, &end);
288
                    Q_ASSERT(SUCCEEDED(hr));
289
                }
290
            }
291
292
            AM_MEDIA_TYPE *type = 0;
293
            hr = sample->GetMediaType(&type);
294
            Q_ASSERT(SUCCEEDED(hr));
295
            hr = out->SetMediaType(type);
296
            Q_ASSERT(SUCCEEDED(hr));
297
298
            hr = out->SetPreroll(sample->IsPreroll());
299
            Q_ASSERT(SUCCEEDED(hr));
300
            hr = out->SetSyncPoint(sample->IsSyncPoint());
301
            Q_ASSERT(SUCCEEDED(hr));
302
303
            BYTE *dest = 0, *src = 0;
304
            hr = out->GetPointer(&dest);
305
            Q_ASSERT(SUCCEEDED(hr));
306
            hr = sample->GetPointer(&src);
307
            Q_ASSERT(SUCCEEDED(hr));
308
309
            qMemCopy(dest, src, sample->GetActualDataLength());
310
311
            return out;
312
        }
313
    }
314
}
315
316
QT_END_NAMESPACE