Started implementing AVI splitter.
[wine/multimedia.git] / dlls / quartz / sysclock.c
blobbef024a14cfd5a8332c2c5b7e1103dc128f556e5
1 /*
2 * Implementation of CLSID_SystemClock.
4 * hidenori@a2.ctktv.ne.jp
5 */
7 #include "config.h"
9 #include "windef.h"
10 #include "winbase.h"
11 #include "wingdi.h"
12 #include "winuser.h"
13 #include "winerror.h"
14 #include "strmif.h"
15 #include "uuids.h"
17 #include "debugtools.h"
18 DEFAULT_DEBUG_CHANNEL(quartz);
20 #include "quartz_private.h"
21 #include "sysclock.h"
24 /***************************************************************************
26 * new/delete for CLSID_SystemClock
30 /* can I use offsetof safely? - FIXME? */
31 static QUARTZ_IFEntry IFEntries[] =
33 { &IID_IReferenceClock, offsetof(CSystemClock,refclk)-offsetof(CSystemClock,unk) },
37 static void QUARTZ_DestroySystemClock(IUnknown* punk)
39 CSystemClock_THIS(punk,unk);
41 CSystemClock_UninitIReferenceClock( This );
44 HRESULT QUARTZ_CreateSystemClock(IUnknown* punkOuter,void** ppobj)
46 CSystemClock* psc;
47 HRESULT hr;
49 TRACE("(%p,%p)\n",punkOuter,ppobj);
51 psc = (CSystemClock*)QUARTZ_AllocObj( sizeof(CSystemClock) );
52 if ( psc == NULL )
53 return E_OUTOFMEMORY;
55 QUARTZ_IUnkInit( &psc->unk, punkOuter );
56 hr = CSystemClock_InitIReferenceClock( psc );
57 if ( FAILED(hr) )
59 QUARTZ_FreeObj( psc );
60 return hr;
63 psc->unk.pEntries = IFEntries;
64 psc->unk.dwEntries = sizeof(IFEntries)/sizeof(IFEntries[0]);
65 psc->unk.pOnFinalRelease = QUARTZ_DestroySystemClock;
67 *ppobj = (void*)(&psc->unk);
69 return S_OK;
73 /***************************************************************************
75 * CLSID_SystemClock::IReferenceClock
79 #define QUARTZ_MSG_ADDTIMER (WM_APP+0)
80 #define QUARTZ_MSG_REMOVETIMER (WM_APP+1)
81 #define QUARTZ_MSG_EXITTHREAD (WM_APP+2)
84 /****************************************************************************/
86 static QUARTZ_TimerEntry* IReferenceClock_AllocTimerEntry(CSystemClock* This)
88 QUARTZ_TimerEntry* pEntry;
89 DWORD dw;
91 pEntry = &This->m_timerEntries[0];
92 for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
94 if ( pEntry->hEvent == (HANDLE)NULL )
95 return pEntry;
96 pEntry ++;
99 return NULL;
102 static QUARTZ_TimerEntry* IReferenceClock_SearchTimer(CSystemClock* This, DWORD dwAdvCookie)
104 QUARTZ_TimerEntry* pEntry;
105 DWORD dw;
107 pEntry = &This->m_timerEntries[0];
108 for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
110 if ( pEntry->hEvent != (HANDLE)NULL &&
111 pEntry->dwAdvCookie == dwAdvCookie )
112 return pEntry;
113 pEntry ++;
116 return NULL;
119 static DWORD IReferenceClock_OnTimerUpdated(CSystemClock* This)
121 QUARTZ_TimerEntry* pEntry;
122 REFERENCE_TIME rtCur;
123 REFERENCE_TIME rtSignal;
124 REFERENCE_TIME rtCount;
125 HRESULT hr;
126 LONG lCount;
127 DWORD dw;
128 DWORD dwTimeout = INFINITE;
129 DWORD dwTimeoutCur;
131 hr = IReferenceClock_GetTime((IReferenceClock*)(&This->refclk),&rtCur);
132 if ( hr != NOERROR )
133 return INFINITE;
135 pEntry = &This->m_timerEntries[0];
136 for ( dw = 0; dw < WINE_QUARTZ_SYSCLOCK_TIMER_MAX; dw++ )
138 if ( pEntry->hEvent != (HANDLE)NULL )
140 rtSignal = pEntry->rtStart + pEntry->rtInterval;
141 if ( rtCur >= rtSignal )
143 if ( pEntry->fPeriodic )
145 rtCount = ((rtCur - pEntry->rtStart) / pEntry->rtInterval);
146 lCount = ( rtCount > (REFERENCE_TIME)0x7fffffff ) ?
147 (LONG)0x7fffffff : (LONG)rtCount;
148 if ( !ReleaseSemaphore( pEntry->hEvent, lCount, NULL ) )
150 while ( lCount > 0 )
152 if ( !ReleaseSemaphore( pEntry->hEvent, 1, NULL ) )
153 break;
156 dwTimeout = 0;
158 else
160 TRACE( "signal an event\n" );
161 SetEvent( pEntry->hEvent );
162 pEntry->hEvent = (HANDLE)NULL;
165 else
167 rtCount = rtSignal - rtCur;
168 /* [100ns] -> [ms] */
169 rtCount = (rtCount+(REFERENCE_TIME)9999)/(REFERENCE_TIME)10000;
170 dwTimeoutCur = (rtCount >= 0xfffffffe) ? (DWORD)0xfffffffe : (DWORD)rtCount;
171 if ( dwTimeout > dwTimeoutCur )
172 dwTimeout = dwTimeoutCur;
175 pEntry ++;
178 return dwTimeout;
181 static
182 DWORD WINAPI IReferenceClock_TimerEntry( LPVOID lpvParam )
184 CSystemClock* This = (CSystemClock*)lpvParam;
185 MSG msg;
186 DWORD dwRes;
187 DWORD dwTimeout;
189 /* initialize the message queue. */
190 PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_NOREMOVE );
191 /* resume the owner thread. */
192 SetEvent( This->m_hEventInit );
194 TRACE( "Enter message loop.\n" );
196 /* message loop. */
197 dwTimeout = INFINITE;
198 while ( 1 )
200 if ( dwTimeout > 0 )
202 dwRes = MsgWaitForMultipleObjects(
203 0, NULL, FALSE,
204 dwTimeout,
205 QS_ALLEVENTS );
208 EnterCriticalSection( &This->m_csClock );
209 dwTimeout = IReferenceClock_OnTimerUpdated(This);
210 LeaveCriticalSection( &This->m_csClock );
211 TRACE( "catch an event / timeout %lu\n", dwTimeout );
213 while ( PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_REMOVE ) )
215 if ( msg.message == WM_QUIT )
216 goto quitthread;
218 if ( msg.hwnd != (HWND)NULL )
220 TranslateMessage( &msg );
221 DispatchMessageA( &msg );
223 else
225 switch ( msg.message )
227 case QUARTZ_MSG_ADDTIMER:
228 case QUARTZ_MSG_REMOVETIMER:
229 dwTimeout = 0;
230 break;
231 case QUARTZ_MSG_EXITTHREAD:
232 PostQuitMessage(0);
233 break;
234 default:
235 FIXME( "invalid message %04u\n", (unsigned)msg.message );
236 break;
242 quitthread:
243 TRACE( "quit thread\n" );
244 return 0;
247 /****************************************************************************/
249 static HRESULT WINAPI
250 IReferenceClock_fnQueryInterface(IReferenceClock* iface,REFIID riid,void** ppobj)
252 CSystemClock_THIS(iface,refclk);
254 TRACE("(%p)->()\n",This);
256 return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
259 static ULONG WINAPI
260 IReferenceClock_fnAddRef(IReferenceClock* iface)
262 CSystemClock_THIS(iface,refclk);
264 TRACE("(%p)->()\n",This);
266 return IUnknown_AddRef(This->unk.punkControl);
269 static ULONG WINAPI
270 IReferenceClock_fnRelease(IReferenceClock* iface)
272 CSystemClock_THIS(iface,refclk);
274 TRACE("(%p)->()\n",This);
276 return IUnknown_Release(This->unk.punkControl);
279 static HRESULT WINAPI
280 IReferenceClock_fnGetTime(IReferenceClock* iface,REFERENCE_TIME* prtTime)
282 CSystemClock_THIS(iface,refclk);
283 DWORD dwTimeCur;
285 TRACE( "(%p)->(%p)\n", This, prtTime );
287 if ( prtTime == NULL )
288 return E_POINTER;
290 EnterCriticalSection( &This->m_csClock );
292 dwTimeCur = GetTickCount();
293 This->m_rtLast += (REFERENCE_TIME)(DWORD)(dwTimeCur - This->m_dwTimeLast) * (REFERENCE_TIME)10000;
295 This->m_dwTimeLast = dwTimeCur;
297 *prtTime = This->m_rtLast;
299 LeaveCriticalSection( &This->m_csClock );
301 return NOERROR;
304 static HRESULT WINAPI
305 IReferenceClock_fnAdviseTime(IReferenceClock* iface,REFERENCE_TIME rtBase,REFERENCE_TIME rtStream,HEVENT hEvent,DWORD_PTR* pdwAdvCookie)
307 CSystemClock_THIS(iface,refclk);
308 QUARTZ_TimerEntry* pEntry;
309 HRESULT hr;
310 REFERENCE_TIME rtCur;
312 TRACE( "(%p)->()\n", This );
314 if ( pdwAdvCookie == NULL )
315 return E_POINTER;
316 if ( hEvent == (HANDLE)NULL )
317 return E_INVALIDARG;
319 EnterCriticalSection( &This->m_csClock );
321 *pdwAdvCookie = (DWORD_PTR)(This->m_dwAdvCookieNext ++);
323 hr = IReferenceClock_GetTime(iface,&rtCur);
324 if ( hr != NOERROR )
325 goto err;
326 if ( rtCur >= (rtBase+rtStream) )
328 SetEvent(hEvent);
329 hr = NOERROR;
330 goto err;
333 pEntry = IReferenceClock_AllocTimerEntry(This);
334 if ( pEntry == NULL )
336 hr = E_FAIL;
337 goto err;
340 pEntry->dwAdvCookie = *pdwAdvCookie;
341 pEntry->fPeriodic = FALSE;
342 pEntry->hEvent = hEvent;
343 pEntry->rtStart = rtBase;
344 pEntry->rtInterval = rtStream;
346 if ( !PostThreadMessageA(
347 This->m_idThreadTimer,
348 QUARTZ_MSG_ADDTIMER,
349 0, 0 ) )
351 pEntry->hEvent = (HANDLE)NULL;
352 hr = E_FAIL;
353 goto err;
356 hr = NOERROR;
357 err:
358 LeaveCriticalSection( &This->m_csClock );
360 return hr;
363 static HRESULT WINAPI
364 IReferenceClock_fnAdvisePeriodic(IReferenceClock* iface,REFERENCE_TIME rtStart,REFERENCE_TIME rtPeriod,HSEMAPHORE hSemaphore,DWORD_PTR* pdwAdvCookie)
366 CSystemClock_THIS(iface,refclk);
367 QUARTZ_TimerEntry* pEntry;
368 HRESULT hr;
370 TRACE( "(%p)->()\n", This );
372 if ( pdwAdvCookie == NULL )
373 return E_POINTER;
374 if ( hSemaphore == (HSEMAPHORE)NULL )
375 return E_INVALIDARG;
377 EnterCriticalSection( &This->m_csClock );
379 *pdwAdvCookie = (DWORD_PTR)(This->m_dwAdvCookieNext ++);
381 pEntry = IReferenceClock_AllocTimerEntry(This);
382 if ( pEntry == NULL )
384 hr = E_FAIL;
385 goto err;
388 pEntry->dwAdvCookie = *pdwAdvCookie;
389 pEntry->fPeriodic = TRUE;
390 pEntry->hEvent = (HANDLE)hSemaphore;
391 pEntry->rtStart = rtStart;
392 pEntry->rtInterval = rtPeriod;
394 if ( !PostThreadMessageA(
395 This->m_idThreadTimer,
396 QUARTZ_MSG_ADDTIMER,
397 0, 0 ) )
399 pEntry->hEvent = (HANDLE)NULL;
400 hr = E_FAIL;
401 goto err;
404 hr = NOERROR;
405 err:
406 LeaveCriticalSection( &This->m_csClock );
408 return hr;
411 static HRESULT WINAPI
412 IReferenceClock_fnUnadvise(IReferenceClock* iface,DWORD_PTR dwAdvCookie)
414 CSystemClock_THIS(iface,refclk);
415 QUARTZ_TimerEntry* pEntry;
417 TRACE( "(%p)->(%lu)\n", This, (DWORD)dwAdvCookie );
419 EnterCriticalSection( &This->m_csClock );
421 pEntry = IReferenceClock_SearchTimer(This,(DWORD)dwAdvCookie);
422 if ( pEntry != NULL )
424 pEntry->hEvent = (HANDLE)NULL;
427 LeaveCriticalSection( &This->m_csClock );
429 return NOERROR;
432 static ICOM_VTABLE(IReferenceClock) irefclk =
434 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
435 /* IUnknown fields */
436 IReferenceClock_fnQueryInterface,
437 IReferenceClock_fnAddRef,
438 IReferenceClock_fnRelease,
439 /* IReferenceClock fields */
440 IReferenceClock_fnGetTime,
441 IReferenceClock_fnAdviseTime,
442 IReferenceClock_fnAdvisePeriodic,
443 IReferenceClock_fnUnadvise,
447 HRESULT CSystemClock_InitIReferenceClock( CSystemClock* psc )
449 HANDLE hEvents[2];
451 TRACE("(%p)\n",psc);
452 ICOM_VTBL(&psc->refclk) = &irefclk;
454 InitializeCriticalSection( &psc->m_csClock );
455 psc->m_dwTimeLast = GetTickCount();
456 psc->m_rtLast = (REFERENCE_TIME)0;
457 psc->m_hThreadTimer = (HANDLE)NULL;
458 psc->m_hEventInit = (HANDLE)NULL;
459 psc->m_idThreadTimer = 0;
460 psc->m_dwAdvCookieNext = 1;
461 ZeroMemory( psc->m_timerEntries, sizeof(psc->m_timerEntries) );
463 psc->m_hEventInit = CreateEventA( NULL, TRUE, FALSE, NULL );
464 if ( psc->m_hEventInit == (HANDLE)NULL )
465 goto err;
467 psc->m_hThreadTimer = CreateThread(
468 NULL, 0,
469 IReferenceClock_TimerEntry,
470 psc, 0, &psc->m_idThreadTimer );
472 if ( psc->m_hThreadTimer == (HANDLE)NULL )
474 CloseHandle( psc->m_hEventInit );
475 psc->m_hEventInit = (HANDLE)NULL;
476 goto err;
479 hEvents[0] = psc->m_hEventInit;
480 hEvents[1] = psc->m_hThreadTimer;
481 if ( WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE )
482 != WAIT_OBJECT_0 )
484 CloseHandle( psc->m_hEventInit );
485 psc->m_hEventInit = (HANDLE)NULL;
486 CloseHandle( psc->m_hThreadTimer );
487 psc->m_hThreadTimer = (HANDLE)NULL;
488 goto err;
491 return NOERROR;
493 err:
494 DeleteCriticalSection( &psc->m_csClock );
495 return E_FAIL;
498 void CSystemClock_UninitIReferenceClock( CSystemClock* psc )
500 TRACE("(%p)\n",psc);
502 if ( psc->m_hThreadTimer != (HANDLE)NULL )
504 if ( PostThreadMessageA(
505 psc->m_idThreadTimer,
506 QUARTZ_MSG_EXITTHREAD,
507 0, 0 ) )
509 WaitForSingleObject( psc->m_hThreadTimer, INFINITE );
511 CloseHandle( psc->m_hThreadTimer );
512 psc->m_hThreadTimer = (HANDLE)NULL;
515 DeleteCriticalSection( &psc->m_csClock );