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