185 lines
7.3 KiB
C
185 lines
7.3 KiB
C
|
//------------------------------------------------------------------------------
|
||
|
// File: RefClock.h
|
||
|
//
|
||
|
// Desc: DirectShow base classes - defines the IReferenceClock interface.
|
||
|
//
|
||
|
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
#ifndef __BASEREFCLOCK__
|
||
|
#define __BASEREFCLOCK__
|
||
|
|
||
|
#include <Schedule.h>
|
||
|
|
||
|
const UINT RESOLUTION = 1; /* High resolution timer */
|
||
|
const INT ADVISE_CACHE = 4; /* Default cache size */
|
||
|
const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */
|
||
|
|
||
|
inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)
|
||
|
{
|
||
|
/* This converts an arbitrary value representing a reference time
|
||
|
into a MILLISECONDS value for use in subsequent system calls */
|
||
|
|
||
|
return (RT / (UNITS / MILLISECONDS));
|
||
|
}
|
||
|
|
||
|
/* This class hierarchy will support an IReferenceClock interface so
|
||
|
that an audio card (or other externally driven clock) can update the
|
||
|
system wide clock that everyone uses.
|
||
|
|
||
|
The interface will be pretty thin with probably just one update method
|
||
|
This interface has not yet been defined.
|
||
|
*/
|
||
|
|
||
|
/* This abstract base class implements the IReferenceClock
|
||
|
* interface. Classes that actually provide clock signals (from
|
||
|
* whatever source) have to be derived from this class.
|
||
|
*
|
||
|
* The abstract class provides implementations for:
|
||
|
* CUnknown support
|
||
|
* locking support (CCritSec)
|
||
|
* client advise code (creates a thread)
|
||
|
*
|
||
|
* Question: what can we do about quality? Change the timer
|
||
|
* resolution to lower the system load? Up the priority of the
|
||
|
* timer thread to force more responsive signals?
|
||
|
*
|
||
|
* During class construction we create a worker thread that is destroyed during
|
||
|
* destuction. This thread executes a series of WaitForSingleObject calls,
|
||
|
* waking up when a command is given to the thread or the next wake up point
|
||
|
* is reached. The wakeup points are determined by clients making Advise
|
||
|
* calls.
|
||
|
*
|
||
|
* Each advise call defines a point in time when they wish to be notified. A
|
||
|
* periodic advise is a series of these such events. We maintain a list of
|
||
|
* advise links and calculate when the nearest event notification is due for.
|
||
|
* We then call WaitForSingleObject with a timeout equal to this time. The
|
||
|
* handle we wait on is used by the class to signal that something has changed
|
||
|
* and that we must reschedule the next event. This typically happens when
|
||
|
* someone comes in and asks for an advise link while we are waiting for an
|
||
|
* event to timeout.
|
||
|
*
|
||
|
* While we are modifying the list of advise requests we
|
||
|
* are protected from interference through a critical section. Clients are NOT
|
||
|
* advised through callbacks. One shot clients have an event set, while
|
||
|
* periodic clients have a semaphore released for each event notification. A
|
||
|
* semaphore allows a client to be kept up to date with the number of events
|
||
|
* actually triggered and be assured that they can't miss multiple events being
|
||
|
* set.
|
||
|
*
|
||
|
* Keeping track of advises is taken care of by the CAMSchedule class.
|
||
|
*/
|
||
|
|
||
|
class CBaseReferenceClock
|
||
|
: public CUnknown, public IReferenceClock, public CCritSec, public IReferenceClockTimerControl
|
||
|
{
|
||
|
protected:
|
||
|
virtual ~CBaseReferenceClock(); // Don't let me be created on the stack!
|
||
|
public:
|
||
|
CBaseReferenceClock(__in_opt LPCTSTR pName,
|
||
|
__inout_opt LPUNKNOWN pUnk,
|
||
|
__inout HRESULT *phr,
|
||
|
__inout_opt CAMSchedule * pSched = 0 );
|
||
|
|
||
|
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv);
|
||
|
|
||
|
DECLARE_IUNKNOWN
|
||
|
|
||
|
/* IReferenceClock methods */
|
||
|
// Derived classes must implement GetPrivateTime(). All our GetTime
|
||
|
// does is call GetPrivateTime and then check so that time does not
|
||
|
// go backwards. A return code of S_FALSE implies that the internal
|
||
|
// clock has gone backwards and GetTime time has halted until internal
|
||
|
// time has caught up. (Don't know if this will be much use to folk,
|
||
|
// but it seems odd not to use the return code for something useful.)
|
||
|
STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime);
|
||
|
// When this is called, it sets m_rtLastGotTime to the time it returns.
|
||
|
|
||
|
/* Provide standard mechanisms for scheduling events */
|
||
|
|
||
|
/* Ask for an async notification that a time has elapsed */
|
||
|
STDMETHODIMP AdviseTime(
|
||
|
REFERENCE_TIME baseTime, // base reference time
|
||
|
REFERENCE_TIME streamTime, // stream offset time
|
||
|
HEVENT hEvent, // advise via this event
|
||
|
__out DWORD_PTR *pdwAdviseCookie// where your cookie goes
|
||
|
);
|
||
|
|
||
|
/* Ask for an asynchronous periodic notification that a time has elapsed */
|
||
|
STDMETHODIMP AdvisePeriodic(
|
||
|
REFERENCE_TIME StartTime, // starting at this time
|
||
|
REFERENCE_TIME PeriodTime, // time between notifications
|
||
|
HSEMAPHORE hSemaphore, // advise via a semaphore
|
||
|
__out DWORD_PTR *pdwAdviseCookie// where your cookie goes
|
||
|
);
|
||
|
|
||
|
/* Cancel a request for notification(s) - if the notification was
|
||
|
* a one shot timer then this function doesn't need to be called
|
||
|
* as the advise is automatically cancelled, however it does no
|
||
|
* harm to explicitly cancel a one-shot advise. It is REQUIRED that
|
||
|
* clients call Unadvise to clear a Periodic advise setting.
|
||
|
*/
|
||
|
|
||
|
STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);
|
||
|
|
||
|
/* Methods for the benefit of derived classes or outer objects */
|
||
|
|
||
|
// GetPrivateTime() is the REAL clock. GetTime is just a cover for
|
||
|
// it. Derived classes will probably override this method but not
|
||
|
// GetTime() itself.
|
||
|
// The important point about GetPrivateTime() is it's allowed to go
|
||
|
// backwards. Our GetTime() will keep returning the LastGotTime
|
||
|
// until GetPrivateTime() catches up.
|
||
|
virtual REFERENCE_TIME GetPrivateTime();
|
||
|
|
||
|
/* Provide a method for correcting drift */
|
||
|
STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );
|
||
|
|
||
|
CAMSchedule * GetSchedule() const { return m_pSchedule; }
|
||
|
|
||
|
// IReferenceClockTimerControl methods
|
||
|
//
|
||
|
// Setting a default of 0 disables the default of 1ms
|
||
|
STDMETHODIMP SetDefaultTimerResolution(
|
||
|
REFERENCE_TIME timerResolution // in 100ns
|
||
|
);
|
||
|
STDMETHODIMP GetDefaultTimerResolution(
|
||
|
__out REFERENCE_TIME* pTimerResolution // in 100ns
|
||
|
);
|
||
|
|
||
|
private:
|
||
|
REFERENCE_TIME m_rtPrivateTime; // Current best estimate of time
|
||
|
DWORD m_dwPrevSystemTime; // Last vaule we got from timeGetTime
|
||
|
REFERENCE_TIME m_rtLastGotTime; // Last time returned by GetTime
|
||
|
REFERENCE_TIME m_rtNextAdvise; // Time of next advise
|
||
|
UINT m_TimerResolution;
|
||
|
|
||
|
#ifdef PERF
|
||
|
int m_idGetSystemTime;
|
||
|
#endif
|
||
|
|
||
|
// Thread stuff
|
||
|
public:
|
||
|
void TriggerThread() // Wakes thread up. Need to do this if
|
||
|
{ // time to next advise needs reevaluating.
|
||
|
EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
BOOL m_bAbort; // Flag used for thread shutdown
|
||
|
HANDLE m_hThread; // Thread handle
|
||
|
|
||
|
HRESULT AdviseThread(); // Method in which the advise thread runs
|
||
|
static DWORD __stdcall AdviseThreadFunction(__in LPVOID); // Function used to get there
|
||
|
|
||
|
protected:
|
||
|
CAMSchedule * m_pSchedule;
|
||
|
|
||
|
void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;
|
||
|
};
|
||
|
|
||
|
#endif
|
||
|
|