AJA NTV2 SDK  18.0.0.2717
NTV2 SDK 18.0.0.2717
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 }
CBaseReferenceClock::AdviseTime
STDMETHODIMP AdviseTime(REFERENCE_TIME baseTime, REFERENCE_TIME streamTime, HEVENT hEvent, __out DWORD_PTR *pdwAdviseCookie)
Definition: refclock.cpp:175
HANDLE
short HANDLE
Definition: ajatypes.h:338
PERFLOG_GETTIME
#define PERFLOG_GETTIME(clock, time)
Definition: dxmperf.h:72
CAMSchedule::Advise
REFERENCE_TIME Advise(const REFERENCE_TIME &rtTime)
Definition: schedule.cpp:143
streams.h
CBaseReferenceClock::GetPrivateTime
virtual REFERENCE_TIME GetPrivateTime()
Definition: refclock.cpp:232
NULL
#define NULL
Definition: ntv2caption608types.h:19
CUnknown::NonDelegatingQueryInterface
STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **)
Definition: combase.cpp:135
CAMSchedule::GetEvent
HANDLE GetEvent() const
Definition: schedule.h:34
LOG_TRACE
@ LOG_TRACE
Definition: wxdebug.h:45
CBaseReferenceClock::CBaseReferenceClock
CBaseReferenceClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr, __inout_opt CAMSchedule *pSched=0)
Definition: refclock.cpp:71
CCritSec::Unlock
void Unlock()
Definition: wxutil.h:52
MAX_TIME
const LONGLONG MAX_TIME
Definition: refclock.h:17
CAutoLock
Definition: wxutil.h:83
CAMSchedule::Unadvise
HRESULT Unadvise(DWORD_PTR dwAdviseCookie)
Definition: schedule.cpp:114
CBaseReferenceClock::Restart
void Restart(IN REFERENCE_TIME rtMinTime=0I64)
Definition: refclock.cpp:135
MILLISECONDS
const LONGLONG MILLISECONDS
Definition: reftime.h:39
CBaseReferenceClock::m_pSchedule
CAMSchedule * m_pSchedule
Definition: refclock.h:178
CBaseReferenceClock::SetDefaultTimerResolution
STDMETHODIMP SetDefaultTimerResolution(REFERENCE_TIME timerResolution)
Definition: refclock.cpp:367
CBaseReferenceClock
Definition: refclock.h:74
ConvertToMilliseconds
LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME &RT)
Definition: refclock.h:19
pName
CHAR * pName
Definition: amvideo.cpp:26
DbgLog
#define DbgLog(_x_)
Definition: wxdebug.h:183
dxmperf.h
CBaseReferenceClock::~CBaseReferenceClock
virtual ~CBaseReferenceClock()
Definition: refclock.cpp:43
riid
__in REFIID riid
Definition: dllentry.cpp:192
MSR_REGISTER
#define MSR_REGISTER(a)
Definition: measure.h:134
EXECUTE_ASSERT
#define EXECUTE_ASSERT(_x_)
Definition: wxdebug.h:207
CUnknown
Definition: combase.h:200
MSR_INTEGER
#define MSR_INTEGER(a, b)
Definition: measure.h:140
PERFLOG_DTOR
#define PERFLOG_DTOR(name, iface)
Definition: dxmperf.h:59
CBaseReferenceClock::NonDelegatingQueryInterface
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
Definition: refclock.cpp:22
CBaseReferenceClock::TriggerThread
void TriggerThread()
Definition: refclock.h:164
PERFLOG_CTOR
#define PERFLOG_CTOR(name, iface)
Definition: dxmperf.h:58
CAMSchedule::DumpLinkedList
void DumpLinkedList()
Definition: schedule.h:123
UNITS
const LONGLONG UNITS
Definition: reftime.h:41
LOG_TIMING
@ LOG_TIMING
Definition: wxdebug.h:44
CAMSchedule::AddAdvisePacket
DWORD_PTR AddAdvisePacket(const REFERENCE_TIME &time1, const REFERENCE_TIME &time2, HANDLE h, BOOL periodic)
Definition: schedule.cpp:78
CCritSec::Lock
void Lock()
Definition: wxutil.h:48
CAMSchedule
Definition: schedule.h:13
CBaseReferenceClock::GetTime
DECLARE_IUNKNOWN STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime)
Definition: refclock.cpp:142
CBaseReferenceClock::GetDefaultTimerResolution
STDMETHODIMP GetDefaultTimerResolution(__out REFERENCE_TIME *pTimerResolution)
Definition: refclock.cpp:392
CBaseReferenceClock::SetTimeDelta
STDMETHODIMP SetTimeDelta(const REFERENCE_TIME &TimeDelta)
Definition: refclock.cpp:264
CBaseReferenceClock::Unadvise
STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie)
Definition: refclock.cpp:226
CheckPointer
#define CheckPointer(p, ret)
Definition: wxdebug.h:225
CAMSchedule::GetAdviseCount
DWORD GetAdviseCount()
Definition: schedule.cpp:65
ASSERT
#define ASSERT(_x_)
Definition: wxdebug.h:205
GetInterface
STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv)
Definition: combase.cpp:213
hr
__out HRESULT & hr
Definition: pstream.cpp:145
CBaseReferenceClock::AdvisePeriodic
STDMETHODIMP AdvisePeriodic(REFERENCE_TIME StartTime, REFERENCE_TIME PeriodTime, HSEMAPHORE hSemaphore, __out DWORD_PTR *pdwAdviseCookie)
Definition: refclock.cpp:205