|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "directshowsamplescheduler.h" |
|
43 |
|
44 #include <QtCore/qcoreevent.h> |
|
45 |
|
46 class DirectShowTimedSample |
|
47 { |
|
48 public: |
|
49 DirectShowTimedSample(IMediaSample *sample) |
|
50 : m_next(0) |
|
51 , m_sample(sample) |
|
52 , m_cookie(0) |
|
53 , m_lastSample(false) |
|
54 { |
|
55 m_sample->AddRef(); |
|
56 } |
|
57 |
|
58 ~DirectShowTimedSample() |
|
59 { |
|
60 m_sample->Release(); |
|
61 } |
|
62 |
|
63 IMediaSample *sample() const { return m_sample; } |
|
64 |
|
65 DirectShowTimedSample *nextSample() const { return m_next; } |
|
66 void setNextSample(DirectShowTimedSample *sample) { Q_ASSERT(!m_next); m_next = sample; } |
|
67 |
|
68 DirectShowTimedSample *remove() { |
|
69 DirectShowTimedSample *next = m_next; delete this; return next; } |
|
70 |
|
71 bool schedule(IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle); |
|
72 void unschedule(IReferenceClock *clock); |
|
73 |
|
74 bool isReady(IReferenceClock *clock) const; |
|
75 |
|
76 bool isLast() const { return m_lastSample; } |
|
77 void setLast() { m_lastSample = true; } |
|
78 |
|
79 private: |
|
80 DirectShowTimedSample *m_next; |
|
81 IMediaSample *m_sample; |
|
82 DWORD_PTR m_cookie; |
|
83 bool m_lastSample; |
|
84 }; |
|
85 |
|
86 bool DirectShowTimedSample::schedule( |
|
87 IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle) |
|
88 { |
|
89 REFERENCE_TIME sampleStartTime; |
|
90 REFERENCE_TIME sampleEndTime; |
|
91 if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) { |
|
92 if (clock->AdviseTime( |
|
93 startTime, sampleStartTime, reinterpret_cast<HEVENT>(handle), &m_cookie) == S_OK) { |
|
94 return true; |
|
95 } |
|
96 } |
|
97 return false; |
|
98 } |
|
99 |
|
100 void DirectShowTimedSample::unschedule(IReferenceClock *clock) |
|
101 { |
|
102 clock->Unadvise(m_cookie); |
|
103 } |
|
104 |
|
105 bool DirectShowTimedSample::isReady(IReferenceClock *clock) const |
|
106 { |
|
107 REFERENCE_TIME sampleStartTime; |
|
108 REFERENCE_TIME sampleEndTime; |
|
109 REFERENCE_TIME currentTime; |
|
110 if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) { |
|
111 if (clock->GetTime(¤tTime) == S_OK) |
|
112 return currentTime >= sampleStartTime; |
|
113 } |
|
114 return true; |
|
115 } |
|
116 |
|
117 DirectShowSampleScheduler::DirectShowSampleScheduler(IUnknown *pin, QObject *parent) |
|
118 : QWinEventNotifier(parent) |
|
119 , m_pin(pin) |
|
120 , m_clock(0) |
|
121 , m_allocator(0) |
|
122 , m_head(0) |
|
123 , m_tail(0) |
|
124 , m_maximumSamples(2) |
|
125 , m_state(Stopped) |
|
126 , m_startTime(0) |
|
127 , m_timeoutEvent(::CreateEvent(0, 0, 0, 0)) |
|
128 { |
|
129 m_semaphore.release(m_maximumSamples); |
|
130 |
|
131 setHandle(m_timeoutEvent); |
|
132 setEnabled(true); |
|
133 } |
|
134 |
|
135 DirectShowSampleScheduler::~DirectShowSampleScheduler() |
|
136 { |
|
137 setEnabled(false); |
|
138 |
|
139 ::CloseHandle(m_timeoutEvent); |
|
140 |
|
141 Q_ASSERT(!m_clock); |
|
142 Q_ASSERT(!m_allocator); |
|
143 } |
|
144 |
|
145 HRESULT DirectShowSampleScheduler::QueryInterface(REFIID riid, void **ppvObject) |
|
146 { |
|
147 return m_pin->QueryInterface(riid, ppvObject); |
|
148 } |
|
149 |
|
150 ULONG DirectShowSampleScheduler::AddRef() |
|
151 { |
|
152 return m_pin->AddRef(); |
|
153 } |
|
154 |
|
155 ULONG DirectShowSampleScheduler::Release() |
|
156 { |
|
157 return m_pin->Release(); |
|
158 } |
|
159 |
|
160 // IMemInputPin |
|
161 HRESULT DirectShowSampleScheduler::GetAllocator(IMemAllocator **ppAllocator) |
|
162 { |
|
163 if (!ppAllocator) { |
|
164 return E_POINTER; |
|
165 } else { |
|
166 QMutexLocker locker(&m_mutex); |
|
167 |
|
168 if (!m_allocator) { |
|
169 return VFW_E_NO_ALLOCATOR; |
|
170 } else { |
|
171 *ppAllocator = m_allocator; |
|
172 |
|
173 return S_OK; |
|
174 } |
|
175 } |
|
176 } |
|
177 |
|
178 HRESULT DirectShowSampleScheduler::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) |
|
179 { |
|
180 Q_UNUSED(bReadOnly); |
|
181 |
|
182 HRESULT hr; |
|
183 ALLOCATOR_PROPERTIES properties; |
|
184 |
|
185 if (!pAllocator) { |
|
186 if (m_allocator) |
|
187 m_allocator->Release(); |
|
188 |
|
189 m_allocator = 0; |
|
190 |
|
191 return S_OK; |
|
192 } else if ((hr = pAllocator->GetProperties(&properties)) != S_OK) { |
|
193 return hr; |
|
194 } else { |
|
195 if (properties.cBuffers == 1) { |
|
196 ALLOCATOR_PROPERTIES actual; |
|
197 |
|
198 properties.cBuffers = 2; |
|
199 if ((hr = pAllocator->SetProperties(&properties, &actual)) != S_OK) |
|
200 return hr; |
|
201 } |
|
202 |
|
203 QMutexLocker locker(&m_mutex); |
|
204 |
|
205 if (m_allocator) |
|
206 m_allocator->Release(); |
|
207 |
|
208 m_allocator = pAllocator; |
|
209 m_allocator->AddRef(); |
|
210 |
|
211 return S_OK; |
|
212 } |
|
213 } |
|
214 |
|
215 HRESULT DirectShowSampleScheduler::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) |
|
216 { |
|
217 if (!pProps) |
|
218 return E_POINTER; |
|
219 |
|
220 pProps->cBuffers = 2; |
|
221 |
|
222 return S_OK; |
|
223 } |
|
224 |
|
225 HRESULT DirectShowSampleScheduler::Receive(IMediaSample *pSample) |
|
226 { |
|
227 if (!pSample) |
|
228 return E_POINTER; |
|
229 |
|
230 m_semaphore.acquire(1); |
|
231 |
|
232 QMutexLocker locker(&m_mutex); |
|
233 |
|
234 if (m_state & Flushing) { |
|
235 m_semaphore.release(1); |
|
236 |
|
237 return S_FALSE; |
|
238 } else if (m_state == Stopped) { |
|
239 m_semaphore.release(); |
|
240 |
|
241 return VFW_E_WRONG_STATE; |
|
242 } else { |
|
243 DirectShowTimedSample *timedSample = new DirectShowTimedSample(pSample); |
|
244 |
|
245 if (m_tail) |
|
246 m_tail->setNextSample(timedSample); |
|
247 else |
|
248 m_head = timedSample; |
|
249 |
|
250 m_tail = timedSample; |
|
251 |
|
252 if (m_state == Running) { |
|
253 if (!timedSample->schedule(m_clock, m_startTime, m_timeoutEvent)) { |
|
254 // Timing information is unavailable, so schedule frames immediately. |
|
255 QMetaObject::invokeMethod(this, "timerActivated", Qt::QueuedConnection); |
|
256 } |
|
257 } else if (m_tail == m_head) { |
|
258 // If this is the first frame make is available. |
|
259 QMetaObject::invokeMethod(this, "timerActivated", Qt::QueuedConnection); |
|
260 } |
|
261 |
|
262 return S_OK; |
|
263 } |
|
264 } |
|
265 |
|
266 HRESULT DirectShowSampleScheduler::ReceiveMultiple( |
|
267 IMediaSample **pSamples, long nSamples, long *nSamplesProcessed) |
|
268 { |
|
269 if (!pSamples || !nSamplesProcessed) |
|
270 return E_POINTER; |
|
271 |
|
272 for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; ++(*nSamplesProcessed)) { |
|
273 HRESULT hr = Receive(pSamples[*nSamplesProcessed]); |
|
274 |
|
275 if (hr != S_OK) |
|
276 return hr; |
|
277 } |
|
278 return S_OK; |
|
279 } |
|
280 |
|
281 HRESULT DirectShowSampleScheduler::ReceiveCanBlock() |
|
282 { |
|
283 return S_OK; |
|
284 } |
|
285 |
|
286 void DirectShowSampleScheduler::run(REFERENCE_TIME startTime) |
|
287 { |
|
288 QMutexLocker locker(&m_mutex); |
|
289 |
|
290 m_state = (m_state & Flushing) | Running; |
|
291 m_startTime = startTime; |
|
292 |
|
293 for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) { |
|
294 sample->schedule(m_clock, m_startTime, m_timeoutEvent); |
|
295 } |
|
296 } |
|
297 |
|
298 void DirectShowSampleScheduler::pause() |
|
299 { |
|
300 QMutexLocker locker(&m_mutex); |
|
301 |
|
302 m_state = (m_state & Flushing) | Paused; |
|
303 |
|
304 for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) |
|
305 sample->unschedule(m_clock); |
|
306 } |
|
307 |
|
308 void DirectShowSampleScheduler::stop() |
|
309 { |
|
310 QMutexLocker locker(&m_mutex); |
|
311 |
|
312 m_state = m_state & Flushing; |
|
313 |
|
314 for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) { |
|
315 sample->unschedule(m_clock); |
|
316 |
|
317 m_semaphore.release(1); |
|
318 } |
|
319 |
|
320 m_head = 0; |
|
321 m_tail = 0; |
|
322 } |
|
323 |
|
324 void DirectShowSampleScheduler::setFlushing(bool flushing) |
|
325 { |
|
326 QMutexLocker locker(&m_mutex); |
|
327 |
|
328 const bool isFlushing = m_state & Flushing; |
|
329 |
|
330 if (isFlushing != flushing) { |
|
331 if (flushing) { |
|
332 m_state |= Flushing; |
|
333 |
|
334 for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) { |
|
335 sample->unschedule(m_clock); |
|
336 |
|
337 m_semaphore.release(1); |
|
338 } |
|
339 m_head = 0; |
|
340 m_tail = 0; |
|
341 } else { |
|
342 m_state &= ~Flushing; |
|
343 } |
|
344 } |
|
345 } |
|
346 |
|
347 void DirectShowSampleScheduler::setClock(IReferenceClock *clock) |
|
348 { |
|
349 QMutexLocker locker(&m_mutex); |
|
350 |
|
351 if (m_clock) |
|
352 m_clock->Release(); |
|
353 |
|
354 m_clock = clock; |
|
355 |
|
356 if (m_clock) |
|
357 m_clock->AddRef(); |
|
358 } |
|
359 |
|
360 IMediaSample *DirectShowSampleScheduler::takeSample(bool *eos) |
|
361 { |
|
362 QMutexLocker locker(&m_mutex); |
|
363 |
|
364 if (m_head && m_head->isReady(m_clock)) { |
|
365 IMediaSample *sample = m_head->sample(); |
|
366 sample->AddRef(); |
|
367 |
|
368 if (m_state == Running) { |
|
369 *eos = m_head->isLast(); |
|
370 |
|
371 m_head = m_head->remove(); |
|
372 |
|
373 if (!m_head) |
|
374 m_tail = 0; |
|
375 |
|
376 m_semaphore.release(1); |
|
377 } |
|
378 |
|
379 return sample; |
|
380 } else { |
|
381 return 0; |
|
382 } |
|
383 } |
|
384 |
|
385 bool DirectShowSampleScheduler::scheduleEndOfStream() |
|
386 { |
|
387 QMutexLocker locker(&m_mutex); |
|
388 |
|
389 if (m_tail) { |
|
390 m_tail->setLast(); |
|
391 |
|
392 return true; |
|
393 } else { |
|
394 return false; |
|
395 } |
|
396 } |
|
397 |
|
398 bool DirectShowSampleScheduler::event(QEvent *event) |
|
399 { |
|
400 if (event->type() == QEvent::WinEventAct) { |
|
401 QObject::event(event); |
|
402 |
|
403 emit sampleReady(); |
|
404 |
|
405 return true; |
|
406 } else { |
|
407 return QWinEventNotifier::event(event); |
|
408 } |
|
409 } |