AJA NTV2 SDK  18.0.0.2122
NTV2 SDK 18.0.0.2122
refclock.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // File: RefClock.cpp
3 //
4 // Desc: DirectShow base classes - implements the IReferenceClock interface.
5 //
6 // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
8 
9 
10 #include <streams.h>
11 #include <limits.h>
12 
13 #ifdef DXMPERF
14 #include "dxmperf.h"
15 #endif // DXMPERF
16 
17 
18 // 'this' used in constructor list
19 #pragma warning(disable:4355)
20 
21 
23  REFIID riid,
24  __deref_out void ** ppv)
25 {
26  HRESULT hr;
27 
28  if (riid == IID_IReferenceClock)
29  {
30  hr = GetInterface((IReferenceClock *) this, ppv);
31  }
32  else if (riid == IID_IReferenceClockTimerControl)
33  {
34  hr = GetInterface((IReferenceClockTimerControl *) this, ppv);
35  }
36  else
37  {
39  }
40  return hr;
41 }
42 
44 {
45 #ifdef DXMPERF
46  PERFLOG_DTOR( L"CBaseReferenceClock", (IReferenceClock *) this );
47 #endif // DXMPERF
48 
49  if (m_TimerResolution) timeEndPeriod(m_TimerResolution);
50 
51  if (m_pSchedule)
52  {
54  }
55 
56  if (m_hThread)
57  {
58  m_bAbort = TRUE;
59  TriggerThread();
60  WaitForSingleObject( m_hThread, INFINITE );
61  EXECUTE_ASSERT( CloseHandle(m_hThread) );
62  m_hThread = 0;
63  EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
64  delete m_pSchedule;
65  }
66 }
67 
68 // A derived class may supply a hThreadEvent if it has its own thread that will take care
69 // of calling the schedulers Advise method. (Refere to CBaseReferenceClock::AdviseThread()
70 // to see what such a thread has to do.)
72  __inout_opt LPUNKNOWN pUnk,
73  __inout HRESULT *phr,
74  __inout_opt CAMSchedule * pShed )
75 : CUnknown( pName, pUnk )
76 , m_rtLastGotTime(0)
77 , m_TimerResolution(0)
78 , m_bAbort( FALSE )
79 , m_pSchedule( pShed ? pShed : new CAMSchedule(CreateEvent(NULL, FALSE, FALSE, NULL)) )
80 , m_hThread(0)
81 {
82 
83 #ifdef DXMPERF
84  PERFLOG_CTOR( pName ? pName : L"CBaseReferenceClock", (IReferenceClock *) this );
85 #endif // DXMPERF
86 
88  if (!m_pSchedule)
89  {
90  *phr = E_OUTOFMEMORY;
91  }
92  else
93  {
94  // Set up the highest resolution timer we can manage
95  TIMECAPS tc;
96  m_TimerResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
97  ? tc.wPeriodMin
98  : 1;
99 
100  timeBeginPeriod(m_TimerResolution);
101 
102  /* Initialise our system times - the derived clock should set the right values */
103  m_dwPrevSystemTime = timeGetTime();
104  m_rtPrivateTime = (UNITS / MILLISECONDS) * m_dwPrevSystemTime;
105 
106  #ifdef PERF
107  m_idGetSystemTime = MSR_REGISTER(TEXT("CBaseReferenceClock::GetTime"));
108  #endif
109 
110  if ( !pShed )
111  {
112  DWORD ThreadID;
113  m_hThread = ::CreateThread(NULL, // Security attributes
114  (DWORD) 0, // Initial stack size
115  AdviseThreadFunction, // Thread start address
116  (LPVOID) this, // Thread parameter
117  (DWORD) 0, // Creation flags
118  &ThreadID); // Thread identifier
119 
120  if (m_hThread)
121  {
122  SetThreadPriority( m_hThread, THREAD_PRIORITY_TIME_CRITICAL );
123  }
124  else
125  {
126  *phr = E_FAIL;
127  EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
128  delete m_pSchedule;
129  m_pSchedule = NULL;
130  }
131  }
132  }
133 }
134 
135 void CBaseReferenceClock::Restart (IN REFERENCE_TIME rtMinTime)
136 {
137  Lock();
138  m_rtLastGotTime = rtMinTime ;
139  Unlock();
140 }
141 
142 STDMETHODIMP CBaseReferenceClock::GetTime(__out REFERENCE_TIME *pTime)
143 {
144  HRESULT hr;
145  if (pTime)
146  {
147  REFERENCE_TIME rtNow;
148  Lock();
149  rtNow = GetPrivateTime();
150  if (rtNow > m_rtLastGotTime)
151  {
152  m_rtLastGotTime = rtNow;
153  hr = S_OK;
154  }
155  else
156  {
157  hr = S_FALSE;
158  }
159  *pTime = m_rtLastGotTime;
160  Unlock();
161  MSR_INTEGER(m_idGetSystemTime, LONG((*pTime) / (UNITS/MILLISECONDS)) );
162 
163 #ifdef DXMPERF
164  PERFLOG_GETTIME( (IReferenceClock *) this, *pTime );
165 #endif // DXMPERF
166 
167  }
168  else hr = E_POINTER;
169 
170  return hr;
171 }
172 
173 /* Ask for an async notification that a time has elapsed */
174 
176  REFERENCE_TIME baseTime, // base reference time
177  REFERENCE_TIME streamTime, // stream offset time
178  HEVENT hEvent, // advise via this event
179  __out DWORD_PTR *pdwAdviseCookie)// where your cookie goes
180 {
181  CheckPointer(pdwAdviseCookie, E_POINTER);
182  *pdwAdviseCookie = 0;
183 
184  // Check that the event is not already set
185  ASSERT(WAIT_TIMEOUT == WaitForSingleObject(HANDLE(hEvent),0));
186 
187  HRESULT hr;
188 
189  const REFERENCE_TIME lRefTime = baseTime + streamTime;
190  if ( lRefTime <= 0 || lRefTime == MAX_TIME )
191  {
192  hr = E_INVALIDARG;
193  }
194  else
195  {
196  *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( lRefTime, 0, HANDLE(hEvent), FALSE );
197  hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
198  }
199  return hr;
200 }
201 
202 
203 /* Ask for an asynchronous periodic notification that a time has elapsed */
204 
206  REFERENCE_TIME StartTime, // starting at this time
207  REFERENCE_TIME PeriodTime, // time between notifications
208  HSEMAPHORE hSemaphore, // advise via a semaphore
209  __out DWORD_PTR *pdwAdviseCookie) // where your cookie goes
210 {
211  CheckPointer(pdwAdviseCookie, E_POINTER);
212  *pdwAdviseCookie = 0;
213 
214  HRESULT hr;
215  if (StartTime > 0 && PeriodTime > 0 && StartTime != MAX_TIME )
216  {
217  *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( StartTime, PeriodTime, HANDLE(hSemaphore), TRUE );
218  hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
219  }
220  else hr = E_INVALIDARG;
221 
222  return hr;
223 }
224 
225 
226 STDMETHODIMP CBaseReferenceClock::Unadvise(DWORD_PTR dwAdviseCookie)
227 {
228  return m_pSchedule->Unadvise(dwAdviseCookie);
229 }
230 
231 
233 {
234  CAutoLock cObjectLock(this);
235 
236 
237  /* If the clock has wrapped then the current time will be less than
238  * the last time we were notified so add on the extra milliseconds
239  *
240  * The time period is long enough so that the likelihood of
241  * successive calls spanning the clock cycle is not considered.
242  */
243 
244  DWORD dwTime = timeGetTime();
245  {
246  m_rtPrivateTime += Int32x32To64(UNITS / MILLISECONDS, (DWORD)(dwTime - m_dwPrevSystemTime));
247  m_dwPrevSystemTime = dwTime;
248  }
249 
250  return m_rtPrivateTime;
251 }
252 
253 
254 /* Adjust the current time by the input value. This allows an
255  external time source to work out some of the latency of the clock
256  system and adjust the "current" time accordingly. The intent is
257  that the time returned to the user is synchronised to a clock
258  source and allows drift to be catered for.
259 
260  For example: if the clock source detects a drift it can pass a delta
261  to the current time rather than having to set an explicit time.
262 */
263 
264 STDMETHODIMP CBaseReferenceClock::SetTimeDelta(const REFERENCE_TIME & TimeDelta)
265 {
266 #ifdef DEBUG
267 
268  // Just break if passed an improper time delta value
269  LONGLONG llDelta = TimeDelta > 0 ? TimeDelta : -TimeDelta;
270  if (llDelta > UNITS * 1000) {
271  DbgLog((LOG_TRACE, 0, TEXT("Bad Time Delta")));
272  //DebugBreak();
273  }
274 
275  // We're going to calculate a "severity" for the time change. Max -1
276  // min 8. We'll then use this as the debug logging level for a
277  // debug log message.
278  const LONG usDelta = LONG(TimeDelta/10); // Delta in micro-secs
279 
280  DWORD delta = abs(usDelta); // varying delta
281  // Severity == 8 - ceil(log<base 8>(abs( micro-secs delta)))
282  int Severity = 8;
283  while ( delta > 0 )
284  {
285  delta >>= 3; // div 8
286  Severity--;
287  }
288 
289  // Sev == 0 => > 2 second delta!
290  DbgLog((LOG_TIMING, Severity < 0 ? 0 : Severity,
291  TEXT("Sev %2i: CSystemClock::SetTimeDelta(%8ld us) %lu -> %lu ms."),
292  Severity, usDelta, DWORD(ConvertToMilliseconds(m_rtPrivateTime)),
293  DWORD(ConvertToMilliseconds(TimeDelta+m_rtPrivateTime)) ));
294 
295  // Don't want the DbgBreak to fire when running stress on debug-builds.
296  #ifdef BREAK_ON_SEVERE_TIME_DELTA
297  if (Severity < 0)
298  DbgBreakPoint(TEXT("SetTimeDelta > 16 seconds!"),
299  TEXT(__FILE__),__LINE__);
300  #endif
301 
302 #endif
303 
304  CAutoLock cObjectLock(this);
305  m_rtPrivateTime += TimeDelta;
306  // If time goes forwards, and we have advises, then we need to
307  // trigger the thread so that it can re-evaluate its wait time.
308  // Since we don't want the cost of the thread switches if the change
309  // is really small, only do it if clock goes forward by more than
310  // 0.5 millisecond. If the time goes backwards, the thread will
311  // wake up "early" (relativly speaking) and will re-evaluate at
312  // that time.
313  if ( TimeDelta > 5000 && m_pSchedule->GetAdviseCount() > 0 ) TriggerThread();
314  return NOERROR;
315 }
316 
317 // Thread stuff
318 
319 DWORD __stdcall CBaseReferenceClock::AdviseThreadFunction(__in LPVOID p)
320 {
321  return DWORD(reinterpret_cast<CBaseReferenceClock*>(p)->AdviseThread());
322 }
323 
324 HRESULT CBaseReferenceClock::AdviseThread()
325 {
326  DWORD dwWait = INFINITE;
327 
328  // The first thing we do is wait until something interesting happens
329  // (meaning a first advise or shutdown). This prevents us calling
330  // GetPrivateTime immediately which is goodness as that is a virtual
331  // routine and the derived class may not yet be constructed. (This
332  // thread is created in the base class constructor.)
333 
334  while ( !m_bAbort )
335  {
336  // Wait for an interesting event to happen
337  DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Delay: %lu ms"), dwWait ));
338  WaitForSingleObject(m_pSchedule->GetEvent(), dwWait);
339  if (m_bAbort) break;
340 
341  // There are several reasons why we need to work from the internal
342  // time, mainly to do with what happens when time goes backwards.
343  // Mainly, it stop us looping madly if an event is just about to
344  // expire when the clock goes backward (i.e. GetTime stop for a
345  // while).
346  const REFERENCE_TIME rtNow = GetPrivateTime();
347 
348  DbgLog((LOG_TIMING, 3,
349  TEXT("CBaseRefClock::AdviseThread() Woke at = %lu ms"),
350  ConvertToMilliseconds(rtNow) ));
351 
352  // We must add in a millisecond, since this is the resolution of our
353  // WaitForSingleObject timer. Failure to do so will cause us to loop
354  // franticly for (approx) 1 a millisecond.
355  m_rtNextAdvise = m_pSchedule->Advise( 10000 + rtNow );
356  LONGLONG llWait = m_rtNextAdvise - rtNow;
357 
358  ASSERT( llWait > 0 );
359 
360  llWait = ConvertToMilliseconds(llWait);
361  // DON'T replace this with a max!! (The type's of these things is VERY important)
362  dwWait = (llWait > REFERENCE_TIME(UINT_MAX)) ? UINT_MAX : DWORD(llWait);
363  };
364  return NOERROR;
365 }
366 
368  REFERENCE_TIME timerResolution // in 100ns
369  )
370 {
371  CAutoLock cObjectLock(this);
372  if( 0 == timerResolution ) {
373  if( m_TimerResolution ) {
374  timeEndPeriod( m_TimerResolution );
375  m_TimerResolution = 0;
376  }
377  } else {
378  TIMECAPS tc;
379  DWORD dwMinResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
380  ? tc.wPeriodMin
381  : 1;
382  DWORD dwResolution = max( dwMinResolution, DWORD(timerResolution / 10000) );
383  if( dwResolution != m_TimerResolution ) {
384  timeEndPeriod(m_TimerResolution);
385  m_TimerResolution = dwResolution;
386  timeBeginPeriod( m_TimerResolution );
387  }
388  }
389  return S_OK;
390 }
391 
393  __out REFERENCE_TIME* pTimerResolution // in 100ns
394  )
395 {
396  if( !pTimerResolution ) {
397  return E_POINTER;
398  }
399  CAutoLock cObjectLock(this);
400  *pTimerResolution = m_TimerResolution * 10000;
401  return S_OK;
402 }
STDMETHODIMP SetTimeDelta(const REFERENCE_TIME &TimeDelta)
Definition: refclock.cpp:264
#define NULL
CHAR * pName
Definition: amvideo.cpp:26
void Unlock()
Definition: wxutil.h:52
STDMETHODIMP AdvisePeriodic(REFERENCE_TIME StartTime, REFERENCE_TIME PeriodTime, HSEMAPHORE hSemaphore, __out DWORD_PTR *pdwAdviseCookie)
Definition: refclock.cpp:205
void Restart(IN REFERENCE_TIME rtMinTime=0I64)
Definition: refclock.cpp:135
STDMETHODIMP SetDefaultTimerResolution(REFERENCE_TIME timerResolution)
Definition: refclock.cpp:367
CBaseReferenceClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr, __inout_opt CAMSchedule *pSched=0)
Definition: refclock.cpp:71
void DumpLinkedList()
Definition: schedule.h:123
#define PERFLOG_GETTIME(clock, time)
Definition: dxmperf.h:72
short HANDLE
Definition: ajatypes.h:285
STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv)
Definition: combase.cpp:213
HANDLE GetEvent() const
Definition: schedule.h:34
DWORD_PTR AddAdvisePacket(const REFERENCE_TIME &time1, const REFERENCE_TIME &time2, HANDLE h, BOOL periodic)
Definition: schedule.cpp:78
virtual ~CBaseReferenceClock()
Definition: refclock.cpp:43
const LONGLONG MAX_TIME
Definition: refclock.h:17
__out HRESULT & hr
Definition: pstream.cpp:145
CAMSchedule * m_pSchedule
Definition: refclock.h:178
#define DbgLog(_x_)
Definition: wxdebug.h:183
const LONGLONG UNITS
Definition: reftime.h:41
DWORD GetAdviseCount()
Definition: schedule.cpp:65
#define EXECUTE_ASSERT(_x_)
Definition: wxdebug.h:207
HRESULT Unadvise(DWORD_PTR dwAdviseCookie)
Definition: schedule.cpp:114
const LONGLONG MILLISECONDS
Definition: reftime.h:39
#define PERFLOG_DTOR(name, iface)
Definition: dxmperf.h:59
#define MSR_INTEGER(a, b)
Definition: measure.h:140
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
Definition: refclock.cpp:22
#define MSR_REGISTER(a)
Definition: measure.h:134
__in REFIID riid
Definition: dllentry.cpp:192
REFERENCE_TIME Advise(const REFERENCE_TIME &rtTime)
Definition: schedule.cpp:143
void TriggerThread()
Definition: refclock.h:164
STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie)
Definition: refclock.cpp:226
LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME &RT)
Definition: refclock.h:19
void Lock()
Definition: wxutil.h:48
STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **)
Definition: combase.cpp:135
#define PERFLOG_CTOR(name, iface)
Definition: dxmperf.h:58
#define CheckPointer(p, ret)
Definition: wxdebug.h:225
DECLARE_IUNKNOWN STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime)
Definition: refclock.cpp:142
#define ASSERT(_x_)
Definition: wxdebug.h:205
virtual REFERENCE_TIME GetPrivateTime()
Definition: refclock.cpp:232
STDMETHODIMP GetDefaultTimerResolution(__out REFERENCE_TIME *pTimerResolution)
Definition: refclock.cpp:392
STDMETHODIMP AdviseTime(REFERENCE_TIME baseTime, REFERENCE_TIME streamTime, HEVENT hEvent, __out DWORD_PTR *pdwAdviseCookie)
Definition: refclock.cpp:175