1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "directshowioreader.h"
41 
42 #include "directshoweventloop.h"
43 #include "directshowglobal.h"
44 #include "directshowiosource.h"
45 
46 #include <QtCore/qcoreapplication.h>
47 #include <QtCore/qcoreevent.h>
48 #include <QtCore/qiodevice.h>
49 #include <QtCore/qthread.h>
50 
51 QT_BEGIN_NAMESPACE
52 
53 class DirectShowSampleRequest
54 {
55 public:
DirectShowSampleRequest(IMediaSample * sample,DWORD_PTR userData,LONGLONG position,LONG length,BYTE * buffer)56     DirectShowSampleRequest(
57             IMediaSample *sample, DWORD_PTR userData, LONGLONG position, LONG length, BYTE *buffer)
58         : sample(sample)
59         , userData(userData)
60         , position(position)
61         , length(length)
62         , buffer(buffer)
63     {
64     }
65 
remove()66     DirectShowSampleRequest *remove() { DirectShowSampleRequest *n = next; delete this; return n; }
67 
68     DirectShowSampleRequest *next = nullptr;
69     IMediaSample *sample;
70     DWORD_PTR userData;
71     LONGLONG position;
72     LONG length;
73     BYTE *buffer;
74     HRESULT result = S_FALSE;
75 };
76 
DirectShowIOReader(QIODevice * device,DirectShowIOSource * source,DirectShowEventLoop * loop)77 DirectShowIOReader::DirectShowIOReader(
78         QIODevice *device, DirectShowIOSource *source, DirectShowEventLoop *loop)
79     : m_source(source)
80     , m_device(device)
81     , m_loop(loop)
82 {
83     moveToThread(device->thread());
84 
85     connect(device, &QIODevice::readyRead, this, &DirectShowIOReader::readyRead);
86 }
87 
~DirectShowIOReader()88 DirectShowIOReader::~DirectShowIOReader()
89 {
90     flushRequests();
91 }
92 
QueryInterface(REFIID riid,void ** ppvObject)93 HRESULT DirectShowIOReader::QueryInterface(REFIID riid, void **ppvObject)
94 {
95     return m_source->QueryInterface(riid, ppvObject);
96 }
97 
AddRef()98 ULONG DirectShowIOReader::AddRef()
99 {
100     return m_source->AddRef();
101 }
102 
Release()103 ULONG DirectShowIOReader::Release()
104 {
105     return m_source->Release();
106 }
107 
108 // IAsyncReader
RequestAllocator(IMemAllocator * pPreferred,ALLOCATOR_PROPERTIES * pProps,IMemAllocator ** ppActual)109 HRESULT DirectShowIOReader::RequestAllocator(
110         IMemAllocator *pPreferred, ALLOCATOR_PROPERTIES *pProps, IMemAllocator **ppActual)
111 {
112     if (!ppActual || !pProps)
113         return E_POINTER;
114 
115     ALLOCATOR_PROPERTIES actualProperties;
116 
117     if (pProps->cbAlign == 0)
118         pProps->cbAlign = 1;
119 
120     if (pPreferred && pPreferred->SetProperties(pProps, &actualProperties) == S_OK) {
121         pPreferred->AddRef();
122 
123         *ppActual = pPreferred;
124         m_source->setAllocator(*ppActual);
125         return S_OK;
126     }
127 
128     *ppActual = com_new<IMemAllocator>(CLSID_MemoryAllocator);
129     if (*ppActual) {
130         if ((*ppActual)->SetProperties(pProps, &actualProperties) == S_OK) {
131             m_source->setAllocator(*ppActual);
132             return S_OK;
133         }
134         (*ppActual)->Release();
135     }
136     ppActual = nullptr;
137     return E_FAIL;
138 }
139 
Request(IMediaSample * pSample,DWORD_PTR dwUser)140 HRESULT DirectShowIOReader::Request(IMediaSample *pSample, DWORD_PTR dwUser)
141 {
142     QMutexLocker locker(&m_mutex);
143 
144     if (!pSample)
145         return E_POINTER;
146     if (m_flushing)
147         return VFW_E_WRONG_STATE;
148 
149     REFERENCE_TIME startTime = 0;
150     REFERENCE_TIME endTime = 0;
151     BYTE *buffer;
152 
153     if (pSample->GetTime(&startTime, &endTime) != S_OK
154         || pSample->GetPointer(&buffer) != S_OK) {
155         return VFW_E_SAMPLE_TIME_NOT_SET;
156     }
157     LONGLONG position = startTime / 10000000;
158     LONG length = qMin<qint64>((endTime - startTime) / 10000000, m_availableLength);
159 
160     auto request = new DirectShowSampleRequest(pSample, dwUser, position, length, buffer);
161 
162     if (m_pendingTail) {
163         m_pendingTail->next = request;
164     } else {
165         m_pendingHead = request;
166         m_loop->postEvent(this, new QEvent(QEvent::User));
167     }
168     m_pendingTail = request;
169 
170     return S_OK;
171 }
172 
WaitForNext(DWORD dwTimeout,IMediaSample ** ppSample,DWORD_PTR * pdwUser)173 HRESULT DirectShowIOReader::WaitForNext(
174         DWORD dwTimeout, IMediaSample **ppSample, DWORD_PTR *pdwUser)
175 {
176     if (!ppSample || !pdwUser)
177         return E_POINTER;
178 
179     QMutexLocker locker(&m_mutex);
180 
181     do {
182         if (m_readyHead) {
183             DirectShowSampleRequest *request = m_readyHead;
184 
185             *ppSample = request->sample;
186             *pdwUser = request->userData;
187 
188             HRESULT hr = request->result;
189 
190             m_readyHead = request->next;
191 
192             if (!m_readyHead)
193                 m_readyTail = nullptr;
194 
195             delete request;
196 
197             return hr;
198         }
199         if (m_flushing) {
200             *ppSample = nullptr;
201             *pdwUser = 0;
202 
203             return VFW_E_WRONG_STATE;
204         }
205     } while (m_wait.wait(&m_mutex, dwTimeout));
206 
207     *ppSample = nullptr;
208     *pdwUser = 0;
209 
210     return VFW_E_TIMEOUT;
211 }
212 
SyncReadAligned(IMediaSample * pSample)213 HRESULT DirectShowIOReader::SyncReadAligned(IMediaSample *pSample)
214 {
215     if (!pSample)
216         return E_POINTER;
217 
218     REFERENCE_TIME startTime = 0;
219     REFERENCE_TIME endTime = 0;
220     BYTE *buffer;
221 
222     if (pSample->GetTime(&startTime, &endTime) != S_OK
223         || pSample->GetPointer(&buffer) != S_OK) {
224         return VFW_E_SAMPLE_TIME_NOT_SET;
225     }
226     LONGLONG position = startTime / 10000000;
227     LONG length = (endTime - startTime) / 10000000;
228 
229     QMutexLocker locker(&m_mutex);
230 
231     if (thread() == QThread::currentThread()) {
232         qint64 bytesRead = 0;
233 
234         HRESULT hr = blockingRead(position, length, buffer, &bytesRead);
235         if (SUCCEEDED(hr))
236             pSample->SetActualDataLength(bytesRead);
237 
238         return hr;
239     }
240     m_synchronousPosition = position;
241     m_synchronousLength = length;
242     m_synchronousBuffer = buffer;
243 
244     m_loop->postEvent(this, new QEvent(QEvent::User));
245 
246     m_wait.wait(&m_mutex);
247 
248     m_synchronousBuffer = nullptr;
249 
250     if (SUCCEEDED(m_synchronousResult))
251         pSample->SetActualDataLength(m_synchronousBytesRead);
252 
253     return m_synchronousResult;
254 }
255 
SyncRead(LONGLONG llPosition,LONG lLength,BYTE * pBuffer)256 HRESULT DirectShowIOReader::SyncRead(LONGLONG llPosition, LONG lLength, BYTE *pBuffer)
257 {
258     if (!pBuffer)
259         return E_POINTER;
260 
261     if (thread() == QThread::currentThread()) {
262         qint64 bytesRead;
263         return blockingRead(llPosition, lLength, pBuffer, &bytesRead);
264     }
265     QMutexLocker locker(&m_mutex);
266 
267     m_synchronousPosition = llPosition;
268     m_synchronousLength = lLength;
269     m_synchronousBuffer = pBuffer;
270 
271     m_loop->postEvent(this, new QEvent(QEvent::User));
272 
273     m_wait.wait(&m_mutex);
274 
275     m_synchronousBuffer = nullptr;
276 
277     return m_synchronousResult;
278 }
279 
Length(LONGLONG * pTotal,LONGLONG * pAvailable)280 HRESULT DirectShowIOReader::Length(LONGLONG *pTotal, LONGLONG *pAvailable)
281 {
282     if (!pTotal || !pAvailable)
283         return E_POINTER;
284 
285     QMutexLocker locker(&m_mutex);
286     *pTotal = m_totalLength;
287     *pAvailable = m_availableLength;
288     return S_OK;
289 }
290 
291 
BeginFlush()292 HRESULT DirectShowIOReader::BeginFlush()
293 {
294     QMutexLocker locker(&m_mutex);
295 
296     if (m_flushing)
297         return S_FALSE;
298 
299     m_flushing = true;
300 
301     flushRequests();
302 
303     m_wait.wakeAll();
304 
305     return S_OK;
306 }
307 
EndFlush()308 HRESULT DirectShowIOReader::EndFlush()
309 {
310     QMutexLocker locker(&m_mutex);
311 
312     if (!m_flushing)
313         return S_FALSE;
314 
315     m_flushing = false;
316 
317     return S_OK;
318 }
319 
customEvent(QEvent * event)320 void DirectShowIOReader::customEvent(QEvent *event)
321 {
322     if (event->type() == QEvent::User) {
323         readyRead();
324     } else {
325         QObject::customEvent(event);
326     }
327 }
328 
readyRead()329 void DirectShowIOReader::readyRead()
330 {
331     QMutexLocker locker(&m_mutex);
332 
333     m_availableLength = m_device->bytesAvailable() + m_device->pos();
334     m_totalLength = m_device->size();
335 
336     if (m_synchronousBuffer) {
337         if (nonBlockingRead(
338                 m_synchronousPosition,
339                 m_synchronousLength,
340                 m_synchronousBuffer,
341                 &m_synchronousBytesRead,
342                 &m_synchronousResult)) {
343             m_wait.wakeAll();
344         }
345     } else {
346         qint64 bytesRead = 0;
347 
348         while (m_pendingHead && nonBlockingRead(
349                 m_pendingHead->position,
350                 m_pendingHead->length,
351                 m_pendingHead->buffer,
352                 &bytesRead,
353             &m_pendingHead->result)) {
354             m_pendingHead->sample->SetActualDataLength(bytesRead);
355 
356             if (m_readyTail)
357                 m_readyTail->next = m_pendingHead;
358             m_readyTail = m_pendingHead;
359 
360             m_pendingHead = m_pendingHead->next;
361 
362             m_readyTail->next = nullptr;
363 
364             if (!m_pendingHead)
365                 m_pendingTail = nullptr;
366 
367             if (!m_readyHead)
368                 m_readyHead = m_readyTail;
369 
370             m_wait.wakeAll();
371         }
372     }
373 }
374 
blockingRead(LONGLONG position,LONG length,BYTE * buffer,qint64 * bytesRead)375 HRESULT DirectShowIOReader::blockingRead(
376         LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead)
377 {
378     *bytesRead = 0;
379 
380     if (qint64(position) > m_device->size())
381         return S_FALSE;
382 
383     const qint64 maxSize = qMin<qint64>(m_device->size(), position + length);
384 
385     while (m_device->bytesAvailable() + m_device->pos() < maxSize) {
386         if (!m_device->waitForReadyRead(-1))
387             return S_FALSE;
388     }
389 
390     if (m_device->pos() != position && !m_device->seek(position))
391         return S_FALSE;
392 
393     const qint64 maxBytes = qMin<qint64>(length, m_device->bytesAvailable());
394 
395     *bytesRead = m_device->read(reinterpret_cast<char *>(buffer), maxBytes);
396 
397     if (*bytesRead != length) {
398         ::memset(buffer + *bytesRead, 0, length - *bytesRead);
399 
400         return S_FALSE;
401     }
402     return S_OK;
403 }
404 
nonBlockingRead(LONGLONG position,LONG length,BYTE * buffer,qint64 * bytesRead,HRESULT * result)405 bool DirectShowIOReader::nonBlockingRead(
406         LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead, HRESULT *result)
407 {
408     const qint64 maxSize = qMin<qint64>(m_device->size(), position + length);
409 
410     if (position > m_device->size()) {
411         *bytesRead = 0;
412         *result = S_FALSE;
413 
414         return true;
415     }
416     if (m_device->bytesAvailable() + m_device->pos() >= maxSize) {
417         if (m_device->pos() != position && !m_device->seek(position)) {
418             *bytesRead = 0;
419             *result = S_FALSE;
420 
421             return true;
422         }
423         const qint64 maxBytes = qMin<qint64>(length, m_device->bytesAvailable());
424 
425         *bytesRead = m_device->read(reinterpret_cast<char *>(buffer), maxBytes);
426 
427         if (*bytesRead != length) {
428             ::memset(buffer + *bytesRead, 0, length - *bytesRead);
429 
430             *result = S_FALSE;
431         } else {
432             *result = S_OK;
433         }
434 
435         return true;
436     }
437     return false;
438 }
439 
flushRequests()440 void DirectShowIOReader::flushRequests()
441 {
442     while (m_pendingHead) {
443         m_pendingHead->result = VFW_E_WRONG_STATE;
444 
445         if (m_readyTail)
446             m_readyTail->next = m_pendingHead;
447 
448         m_readyTail = m_pendingHead;
449 
450         m_pendingHead = m_pendingHead->next;
451 
452         m_readyTail->next = nullptr;
453 
454         if (!m_pendingHead)
455             m_pendingTail = nullptr;
456 
457         if (!m_readyHead)
458             m_readyHead = m_readyTail;
459     }
460 }
461 
462 QT_END_NAMESPACE
463