Warning: That file was not part of the compilation database. It may have many parsing errors.
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 Mobility Components. |
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 "mfstream.h" |
41 | #include <QtCore/qcoreapplication.h> |
42 | |
43 | //MFStream is added for supporting QIODevice type of media source. |
44 | //It is used to delegate invocations from media foundation(through IMFByteStream) to QIODevice. |
45 | |
46 | MFStream::MFStream(QIODevice *stream, bool ownStream) |
47 | : m_cRef(1) |
48 | , m_stream(stream) |
49 | , m_ownStream(ownStream) |
50 | , m_currentReadResult(0) |
51 | { |
52 | //Move to the thread of the stream object |
53 | //to make sure invocations on stream |
54 | //are happened in the same thread of stream object |
55 | this->moveToThread(stream->thread()); |
56 | connect(stream, SIGNAL(readyRead()), this, SLOT(handleReadyRead())); |
57 | } |
58 | |
59 | MFStream::~MFStream() |
60 | { |
61 | if (m_currentReadResult) |
62 | m_currentReadResult->Release(); |
63 | if (m_ownStream) |
64 | m_stream->deleteLater(); |
65 | } |
66 | |
67 | //from IUnknown |
68 | STDMETHODIMP MFStream::QueryInterface(REFIID riid, LPVOID *ppvObject) |
69 | { |
70 | if (!ppvObject) |
71 | return E_POINTER; |
72 | if (riid == IID_IMFByteStream) { |
73 | *ppvObject = static_cast<IMFByteStream*>(this); |
74 | } else if (riid == IID_IUnknown) { |
75 | *ppvObject = static_cast<IUnknown*>(this); |
76 | } else { |
77 | *ppvObject = NULL; |
78 | return E_NOINTERFACE; |
79 | } |
80 | AddRef(); |
81 | return S_OK; |
82 | } |
83 | |
84 | STDMETHODIMP_(ULONG) MFStream::AddRef(void) |
85 | { |
86 | return InterlockedIncrement(&m_cRef); |
87 | } |
88 | |
89 | STDMETHODIMP_(ULONG) MFStream::Release(void) |
90 | { |
91 | LONG cRef = InterlockedDecrement(&m_cRef); |
92 | if (cRef == 0) { |
93 | this->deleteLater(); |
94 | } |
95 | return cRef; |
96 | } |
97 | |
98 | |
99 | //from IMFByteStream |
100 | STDMETHODIMP MFStream::GetCapabilities(DWORD *pdwCapabilities) |
101 | { |
102 | if (!pdwCapabilities) |
103 | return E_INVALIDARG; |
104 | *pdwCapabilities = MFBYTESTREAM_IS_READABLE; |
105 | if (!m_stream->isSequential()) |
106 | *pdwCapabilities |= MFBYTESTREAM_IS_SEEKABLE; |
107 | return S_OK; |
108 | } |
109 | |
110 | STDMETHODIMP MFStream::GetLength(QWORD *pqwLength) |
111 | { |
112 | if (!pqwLength) |
113 | return E_INVALIDARG; |
114 | QMutexLocker locker(&m_mutex); |
115 | *pqwLength = QWORD(m_stream->size()); |
116 | return S_OK; |
117 | } |
118 | |
119 | STDMETHODIMP MFStream::SetLength(QWORD) |
120 | { |
121 | return E_NOTIMPL; |
122 | } |
123 | |
124 | STDMETHODIMP MFStream::GetCurrentPosition(QWORD *pqwPosition) |
125 | { |
126 | if (!pqwPosition) |
127 | return E_INVALIDARG; |
128 | QMutexLocker locker(&m_mutex); |
129 | *pqwPosition = m_stream->pos(); |
130 | return S_OK; |
131 | } |
132 | |
133 | STDMETHODIMP MFStream::SetCurrentPosition(QWORD qwPosition) |
134 | { |
135 | QMutexLocker locker(&m_mutex); |
136 | //SetCurrentPosition may happend during the BeginRead/EndRead pair, |
137 | //refusing to execute SetCurrentPosition during that time seems to be |
138 | //the simplest workable solution |
139 | if (m_currentReadResult) |
140 | return S_FALSE; |
141 | |
142 | bool seekOK = m_stream->seek(qint64(qwPosition)); |
143 | if (seekOK) |
144 | return S_OK; |
145 | else |
146 | return S_FALSE; |
147 | } |
148 | |
149 | STDMETHODIMP MFStream::IsEndOfStream(BOOL *pfEndOfStream) |
150 | { |
151 | if (!pfEndOfStream) |
152 | return E_INVALIDARG; |
153 | QMutexLocker locker(&m_mutex); |
154 | *pfEndOfStream = m_stream->atEnd() ? TRUE : FALSE; |
155 | return S_OK; |
156 | } |
157 | |
158 | STDMETHODIMP MFStream::Read(BYTE *pb, ULONG cb, ULONG *pcbRead) |
159 | { |
160 | QMutexLocker locker(&m_mutex); |
161 | qint64 read = m_stream->read((char*)(pb), qint64(cb)); |
162 | if (pcbRead) |
163 | *pcbRead = ULONG(read); |
164 | return S_OK; |
165 | } |
166 | |
167 | STDMETHODIMP MFStream::BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback, |
168 | IUnknown *punkState) |
169 | { |
170 | if (!pCallback || !pb) |
171 | return E_INVALIDARG; |
172 | |
173 | Q_ASSERT(m_currentReadResult == NULL); |
174 | |
175 | AsyncReadState *state = new (std::nothrow) AsyncReadState(pb, cb); |
176 | if (state == NULL) |
177 | return E_OUTOFMEMORY; |
178 | |
179 | HRESULT hr = MFCreateAsyncResult(state, pCallback, punkState, &m_currentReadResult); |
180 | state->Release(); |
181 | if (FAILED(hr)) |
182 | return hr; |
183 | |
184 | QCoreApplication::postEvent(this, new QEvent(QEvent::User)); |
185 | return hr; |
186 | } |
187 | |
188 | STDMETHODIMP MFStream::EndRead(IMFAsyncResult* pResult, ULONG *pcbRead) |
189 | { |
190 | if (!pcbRead) |
191 | return E_INVALIDARG; |
192 | IUnknown *pUnk; |
193 | pResult->GetObject(&pUnk); |
194 | AsyncReadState *state = static_cast<AsyncReadState*>(pUnk); |
195 | *pcbRead = state->bytesRead(); |
196 | pUnk->Release(); |
197 | |
198 | m_currentReadResult->Release(); |
199 | m_currentReadResult = NULL; |
200 | |
201 | return S_OK; |
202 | } |
203 | |
204 | STDMETHODIMP MFStream::Write(const BYTE *, ULONG, ULONG *) |
205 | { |
206 | return E_NOTIMPL; |
207 | } |
208 | |
209 | STDMETHODIMP MFStream::BeginWrite(const BYTE *, ULONG , |
210 | IMFAsyncCallback *, |
211 | IUnknown *) |
212 | { |
213 | return E_NOTIMPL; |
214 | } |
215 | |
216 | STDMETHODIMP MFStream::EndWrite(IMFAsyncResult *, |
217 | ULONG *) |
218 | { |
219 | return E_NOTIMPL; |
220 | } |
221 | |
222 | STDMETHODIMP MFStream::Seek( |
223 | MFBYTESTREAM_SEEK_ORIGIN SeekOrigin, |
224 | LONGLONG llSeekOffset, |
225 | DWORD, |
226 | QWORD *pqwCurrentPosition) |
227 | { |
228 | QMutexLocker locker(&m_mutex); |
229 | if (m_currentReadResult) |
230 | return S_FALSE; |
231 | |
232 | qint64 pos = qint64(llSeekOffset); |
233 | switch (SeekOrigin) { |
234 | case msoBegin: |
235 | break; |
236 | case msoCurrent: |
237 | pos += m_stream->pos(); |
238 | break; |
239 | } |
240 | bool seekOK = m_stream->seek(pos); |
241 | if (pqwCurrentPosition) |
242 | *pqwCurrentPosition = pos; |
243 | if (seekOK) |
244 | return S_OK; |
245 | else |
246 | return S_FALSE; |
247 | } |
248 | |
249 | STDMETHODIMP MFStream::Flush() |
250 | { |
251 | return E_NOTIMPL; |
252 | } |
253 | |
254 | STDMETHODIMP MFStream::Close() |
255 | { |
256 | QMutexLocker locker(&m_mutex); |
257 | if (m_ownStream) |
258 | m_stream->close(); |
259 | return S_OK; |
260 | } |
261 | |
262 | void MFStream::doRead() |
263 | { |
264 | bool readDone = true; |
265 | IUnknown *pUnk = NULL; |
266 | HRESULT hr = m_currentReadResult->GetObject(&pUnk); |
267 | if (SUCCEEDED(hr)) { |
268 | //do actual read |
269 | AsyncReadState *state = static_cast<AsyncReadState*>(pUnk); |
270 | ULONG cbRead; |
271 | Read(state->pb(), state->cb() - state->bytesRead(), &cbRead); |
272 | pUnk->Release(); |
273 | |
274 | state->setBytesRead(cbRead + state->bytesRead()); |
275 | if (state->cb() > state->bytesRead() && !m_stream->atEnd()) { |
276 | readDone = false; |
277 | } |
278 | } |
279 | |
280 | if (readDone) { |
281 | //now inform the original caller |
282 | m_currentReadResult->SetStatus(hr); |
283 | MFInvokeCallback(m_currentReadResult); |
284 | } |
285 | } |
286 | |
287 | |
288 | void MFStream::handleReadyRead() |
289 | { |
290 | doRead(); |
291 | } |
292 | |
293 | void MFStream::customEvent(QEvent *event) |
294 | { |
295 | if (event->type() != QEvent::User) { |
296 | QObject::customEvent(event); |
297 | return; |
298 | } |
299 | doRead(); |
300 | } |
301 | |
302 | //AsyncReadState is a helper class used in BeginRead for asynchronous operation |
303 | //to record some BeginRead parameters, so these parameters could be |
304 | //used later when actually executing the read operation in another thread. |
305 | MFStream::AsyncReadState::AsyncReadState(BYTE *pb, ULONG cb) |
306 | : m_cRef(1) |
307 | , m_pb(pb) |
308 | , m_cb(cb) |
309 | , m_cbRead(0) |
310 | { |
311 | } |
312 | |
313 | //from IUnknown |
314 | STDMETHODIMP MFStream::AsyncReadState::QueryInterface(REFIID riid, LPVOID *ppvObject) |
315 | { |
316 | if (!ppvObject) |
317 | return E_POINTER; |
318 | |
319 | if (riid == IID_IUnknown) { |
320 | *ppvObject = static_cast<IUnknown*>(this); |
321 | } else { |
322 | *ppvObject = NULL; |
323 | return E_NOINTERFACE; |
324 | } |
325 | AddRef(); |
326 | return S_OK; |
327 | } |
328 | |
329 | STDMETHODIMP_(ULONG) MFStream::AsyncReadState::AddRef(void) |
330 | { |
331 | return InterlockedIncrement(&m_cRef); |
332 | } |
333 | |
334 | STDMETHODIMP_(ULONG) MFStream::AsyncReadState::Release(void) |
335 | { |
336 | LONG cRef = InterlockedDecrement(&m_cRef); |
337 | if (cRef == 0) |
338 | delete this; |
339 | // For thread safety, return a temporary variable. |
340 | return cRef; |
341 | } |
342 | |
343 | BYTE* MFStream::AsyncReadState::pb() const |
344 | { |
345 | return m_pb; |
346 | } |
347 | |
348 | ULONG MFStream::AsyncReadState::cb() const |
349 | { |
350 | return m_cb; |
351 | } |
352 | |
353 | ULONG MFStream::AsyncReadState::bytesRead() const |
354 | { |
355 | return m_cbRead; |
356 | } |
357 | |
358 | void MFStream::AsyncReadState::setBytesRead(ULONG cbRead) |
359 | { |
360 | m_cbRead = cbRead; |
361 | } |
362 |
Warning: That file was not part of the compilation database. It may have many parsing errors.