1 //------------------------------------------------------------------------------
2 // File: RefClock.h
3 //
4 // Desc: DirectShow base classes - defines the IReferenceClock interface.
5 //
6 // Copyright (c) 1992-2002 Microsoft Corporation.  All rights reserved.
7 //------------------------------------------------------------------------------
8 
9 
10 #ifndef __BASEREFCLOCK__
11 #define __BASEREFCLOCK__
12 
13 #include "dsschedule.h"
14 
15 const UINT RESOLUTION = 1;                      /* High resolution timer */
16 const INT ADVISE_CACHE = 4;                     /* Default cache size */
17 const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF;   /* Maximum LONGLONG value */
18 
ConvertToMilliseconds(const REFERENCE_TIME & RT)19 inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)
20 {
21     /* This converts an arbitrary value representing a reference time
22        into a MILLISECONDS value for use in subsequent system calls */
23 
24     return (RT / (UNITS / MILLISECONDS));
25 }
26 
27 /* This class hierarchy will support an IReferenceClock interface so
28    that an audio card (or other externally driven clock) can update the
29    system wide clock that everyone uses.
30 
31    The interface will be pretty thin with probably just one update method
32    This interface has not yet been defined.
33  */
34 
35 /* This abstract base class implements the IReferenceClock
36  * interface.  Classes that actually provide clock signals (from
37  * whatever source) have to be derived from this class.
38  *
39  * The abstract class provides implementations for:
40  * 	CUnknown support
41  *      locking support (CCritSec)
42  *	client advise code (creates a thread)
43  *
44  * Question: what can we do about quality?  Change the timer
45  * resolution to lower the system load?  Up the priority of the
46  * timer thread to force more responsive signals?
47  *
48  * During class construction we create a worker thread that is destroyed during
49  * destuction.  This thread executes a series of WaitForSingleObject calls,
50  * waking up when a command is given to the thread or the next wake up point
51  * is reached.  The wakeup points are determined by clients making Advise
52  * calls.
53  *
54  * Each advise call defines a point in time when they wish to be notified.  A
55  * periodic advise is a series of these such events.  We maintain a list of
56  * advise links and calculate when the nearest event notification is due for.
57  * We then call WaitForSingleObject with a timeout equal to this time.  The
58  * handle we wait on is used by the class to signal that something has changed
59  * and that we must reschedule the next event.  This typically happens when
60  * someone comes in and asks for an advise link while we are waiting for an
61  * event to timeout.
62  *
63  * While we are modifying the list of advise requests we
64  * are protected from interference through a critical section.  Clients are NOT
65  * advised through callbacks.  One shot clients have an event set, while
66  * periodic clients have a semaphore released for each event notification.  A
67  * semaphore allows a client to be kept up to date with the number of events
68  * actually triggered and be assured that they can't miss multiple events being
69  * set.
70  *
71  * Keeping track of advises is taken care of by the CAMSchedule class.
72  */
73 
74 class CBaseReferenceClock
75 : public CUnknown, public IReferenceClock, public CCritSec
76 {
77 protected:
78     virtual ~CBaseReferenceClock();     // Don't let me be created on the stack!
79 public:
80     CBaseReferenceClock(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, CAMSchedule * pSched = 0 );
81 
82     STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void ** ppv);
83 
84     DECLARE_IUNKNOWN
85 
86     /* IReferenceClock methods */
87     // Derived classes must implement GetPrivateTime().  All our GetTime
88     // does is call GetPrivateTime and then check so that time does not
89     // go backwards.  A return code of S_FALSE implies that the internal
90     // clock has gone backwards and GetTime time has halted until internal
91     // time has caught up. (Don't know if this will be much use to folk,
92     // but it seems odd not to use the return code for something useful.)
93     STDMETHODIMP GetTime(REFERENCE_TIME *pTime);
94     // When this is called, it sets m_rtLastGotTime to the time it returns.
95 
96     /* Provide standard mechanisms for scheduling events */
97 
98     /* Ask for an async notification that a time has elapsed */
99     STDMETHODIMP AdviseTime(
100         REFERENCE_TIME baseTime,        // base reference time
101         REFERENCE_TIME streamTime,      // stream offset time
102         HEVENT hEvent,                  // advise via this event
103         DWORD_PTR *pdwAdviseCookie          // where your cookie goes
104     );
105 
106     /* Ask for an asynchronous periodic notification that a time has elapsed */
107     STDMETHODIMP AdvisePeriodic(
108         REFERENCE_TIME StartTime,       // starting at this time
109         REFERENCE_TIME PeriodTime,      // time between notifications
110         HSEMAPHORE hSemaphore,          // advise via a semaphore
111         DWORD_PTR *pdwAdviseCookie          // where your cookie goes
112     );
113 
114     /* Cancel a request for notification(s) - if the notification was
115      * a one shot timer then this function doesn't need to be called
116      * as the advise is automatically cancelled, however it does no
117      * harm to explicitly cancel a one-shot advise.  It is REQUIRED that
118      * clients call Unadvise to clear a Periodic advise setting.
119      */
120 
121     STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);
122 
123     /* Methods for the benefit of derived classes or outer objects */
124 
125     // GetPrivateTime() is the REAL clock.  GetTime is just a cover for
126     // it.  Derived classes will probably override this method but not
127     // GetTime() itself.
128     // The important point about GetPrivateTime() is it's allowed to go
129     // backwards.  Our GetTime() will keep returning the LastGotTime
130     // until GetPrivateTime() catches up.
131     virtual REFERENCE_TIME GetPrivateTime();
132 
133     /* Provide a method for correcting drift */
134     STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );
135 
GetSchedule()136     CAMSchedule * GetSchedule() const { return m_pSchedule; }
137 
138 private:
139     REFERENCE_TIME m_rtPrivateTime;     // Current best estimate of time
140     DWORD          m_dwPrevSystemTime;  // Last vaule we got from timeGetTime
141     REFERENCE_TIME m_rtLastGotTime;     // Last time returned by GetTime
142     REFERENCE_TIME m_rtNextAdvise;      // Time of next advise
143     UINT           m_TimerResolution;
144 
145 #ifdef PERF
146     int m_idGetSystemTime;
147 #endif
148 
149 // Thread stuff
150 public:
TriggerThread()151     void TriggerThread()                	// Wakes thread up.  Need to do this if
152     {						// time to next advise needs reevaluating.
153 	EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));
154     }
155 
156 
157 private:
158     BOOL           m_bAbort;            // Flag used for thread shutdown
159     HANDLE         m_hThread;           // Thread handle
160 
161     HRESULT AdviseThread();             // Method in which the advise thread runs
162     static DWORD __stdcall AdviseThreadFunction(LPVOID); // Function used to get there
163 
164 protected:
165     CAMSchedule * const m_pSchedule;
166 };
167 
168 #endif
169 
170