//------------------------------------------------------------------------------ | |
// 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 | |