410 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			410 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								//------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								// File: RefClock.cpp
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Desc: DirectShow base classes - implements the IReferenceClock interface.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
							 | 
						||
| 
								 | 
							
								//------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#undef NOMINMAX
							 | 
						||
| 
								 | 
							
								#include <streams.h>
							 | 
						||
| 
								 | 
							
								#include <limits.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef DXMPERF
							 | 
						||
| 
								 | 
							
								#include "dxmperf.h"
							 | 
						||
| 
								 | 
							
								#endif // DXMPERF
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef max
							 | 
						||
| 
								 | 
							
								#define max(a, b) (((a) > (b)) ? (a) : (b))
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef min
							 | 
						||
| 
								 | 
							
								#define min(a, b) (((a) < (b)) ? (a) : (b))
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 'this' used in constructor list
							 | 
						||
| 
								 | 
							
								#pragma warning(disable:4355)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseReferenceClock::NonDelegatingQueryInterface(
							 | 
						||
| 
								 | 
							
								    REFIID riid,
							 | 
						||
| 
								 | 
							
								    __deref_out void ** ppv)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    HRESULT hr;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (riid == IID_IReferenceClock)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        hr = GetInterface((IReferenceClock *) this, ppv);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (riid == IID_IReferenceClockTimerControl)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        hr = GetInterface((IReferenceClockTimerControl *) this, ppv);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        hr = CUnknown::NonDelegatingQueryInterface(riid, ppv);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return hr;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CBaseReferenceClock::~CBaseReferenceClock()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#ifdef DXMPERF
							 | 
						||
| 
								 | 
							
								    PERFLOG_DTOR( L"CBaseReferenceClock", (IReferenceClock *) this );
							 | 
						||
| 
								 | 
							
								#endif // DXMPERF
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_TimerResolution) timeEndPeriod(m_TimerResolution);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pSchedule)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        m_pSchedule->DumpLinkedList();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_hThread)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        m_bAbort = TRUE;
							 | 
						||
| 
								 | 
							
								        TriggerThread();
							 | 
						||
| 
								 | 
							
								        WaitForSingleObject( m_hThread, INFINITE );
							 | 
						||
| 
								 | 
							
								        EXECUTE_ASSERT( CloseHandle(m_hThread) );
							 | 
						||
| 
								 | 
							
								        m_hThread = 0;
							 | 
						||
| 
								 | 
							
								        EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
							 | 
						||
| 
								 | 
							
									delete m_pSchedule;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// A derived class may supply a hThreadEvent if it has its own thread that will take care
							 | 
						||
| 
								 | 
							
								// of calling the schedulers Advise method.  (Refere to CBaseReferenceClock::AdviseThread()
							 | 
						||
| 
								 | 
							
								// to see what such a thread has to do.)
							 | 
						||
| 
								 | 
							
								CBaseReferenceClock::CBaseReferenceClock( __in_opt LPCTSTR pName, 
							 | 
						||
| 
								 | 
							
								                                          __inout_opt LPUNKNOWN pUnk, 
							 | 
						||
| 
								 | 
							
								                                          __inout HRESULT *phr, 
							 | 
						||
| 
								 | 
							
								                                          __inout_opt CAMSchedule * pShed )
							 | 
						||
| 
								 | 
							
								: CUnknown( pName, pUnk )
							 | 
						||
| 
								 | 
							
								, m_rtLastGotTime(0)
							 | 
						||
| 
								 | 
							
								, m_TimerResolution(0)
							 | 
						||
| 
								 | 
							
								, m_bAbort( FALSE )
							 | 
						||
| 
								 | 
							
								, m_pSchedule( pShed ? pShed : new CAMSchedule(CreateEvent(NULL, FALSE, FALSE, NULL)) )
							 | 
						||
| 
								 | 
							
								, m_hThread(0)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef DXMPERF
							 | 
						||
| 
								 | 
							
								    PERFLOG_CTOR( pName ? pName : L"CBaseReferenceClock", (IReferenceClock *) this );
							 | 
						||
| 
								 | 
							
								#endif // DXMPERF
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ASSERT(m_pSchedule);
							 | 
						||
| 
								 | 
							
								    if (!m_pSchedule)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        *phr = E_OUTOFMEMORY;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // Set up the highest resolution timer we can manage
							 | 
						||
| 
								 | 
							
								        TIMECAPS tc;
							 | 
						||
| 
								 | 
							
								        m_TimerResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
							 | 
						||
| 
								 | 
							
								                            ? tc.wPeriodMin
							 | 
						||
| 
								 | 
							
								                            : 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        timeBeginPeriod(m_TimerResolution);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /* Initialise our system times - the derived clock should set the right values */
							 | 
						||
| 
								 | 
							
								        m_dwPrevSystemTime = timeGetTime();
							 | 
						||
| 
								 | 
							
								        m_rtPrivateTime = (UNITS / MILLISECONDS) * m_dwPrevSystemTime;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #ifdef PERF
							 | 
						||
| 
								 | 
							
								            m_idGetSystemTime = MSR_REGISTER(TEXT("CBaseReferenceClock::GetTime"));
							 | 
						||
| 
								 | 
							
								        #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ( !pShed )
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            DWORD ThreadID;
							 | 
						||
| 
								 | 
							
								            m_hThread = ::CreateThread(NULL,                  // Security attributes
							 | 
						||
| 
								 | 
							
								                                       (DWORD) 0,             // Initial stack size
							 | 
						||
| 
								 | 
							
								                                       AdviseThreadFunction,  // Thread start address
							 | 
						||
| 
								 | 
							
								                                       (LPVOID) this,         // Thread parameter
							 | 
						||
| 
								 | 
							
								                                       (DWORD) 0,             // Creation flags
							 | 
						||
| 
								 | 
							
								                                       &ThreadID);            // Thread identifier
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (m_hThread)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                SetThreadPriority( m_hThread, THREAD_PRIORITY_TIME_CRITICAL );
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                *phr = E_FAIL;
							 | 
						||
| 
								 | 
							
								                EXECUTE_ASSERT( CloseHandle(m_pSchedule->GetEvent()) );
							 | 
						||
| 
								 | 
							
								                delete m_pSchedule;
							 | 
						||
| 
								 | 
							
								                m_pSchedule = NULL;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CBaseReferenceClock::Restart (IN REFERENCE_TIME rtMinTime)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    Lock();
							 | 
						||
| 
								 | 
							
								    m_rtLastGotTime = rtMinTime ;
							 | 
						||
| 
								 | 
							
								    Unlock();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseReferenceClock::GetTime(__out REFERENCE_TIME *pTime)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    HRESULT hr;
							 | 
						||
| 
								 | 
							
								    if (pTime)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        REFERENCE_TIME rtNow;
							 | 
						||
| 
								 | 
							
								        Lock();
							 | 
						||
| 
								 | 
							
								        rtNow = GetPrivateTime();
							 | 
						||
| 
								 | 
							
								        if (rtNow > m_rtLastGotTime)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            m_rtLastGotTime = rtNow;
							 | 
						||
| 
								 | 
							
								            hr = S_OK;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            hr = S_FALSE;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        *pTime = m_rtLastGotTime;
							 | 
						||
| 
								 | 
							
								        Unlock();
							 | 
						||
| 
								 | 
							
								        MSR_INTEGER(m_idGetSystemTime, LONG((*pTime) / (UNITS/MILLISECONDS)) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef DXMPERF
							 | 
						||
| 
								 | 
							
								        PERFLOG_GETTIME( (IReferenceClock *) this, *pTime );
							 | 
						||
| 
								 | 
							
								#endif // DXMPERF
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else hr = E_POINTER;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return hr;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Ask for an async notification that a time has elapsed */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseReferenceClock::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
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(pdwAdviseCookie, E_POINTER);
							 | 
						||
| 
								 | 
							
								    *pdwAdviseCookie = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Check that the event is not already set
							 | 
						||
| 
								 | 
							
								    ASSERT(WAIT_TIMEOUT == WaitForSingleObject(HANDLE(hEvent),0));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    HRESULT hr;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const REFERENCE_TIME lRefTime = baseTime + streamTime;
							 | 
						||
| 
								 | 
							
								    if ( lRefTime <= 0 || lRefTime == MAX_TIME )
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        hr = E_INVALIDARG;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( lRefTime, 0, HANDLE(hEvent), FALSE );
							 | 
						||
| 
								 | 
							
								        hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return hr;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Ask for an asynchronous periodic notification that a time has elapsed */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseReferenceClock::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
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(pdwAdviseCookie, E_POINTER);
							 | 
						||
| 
								 | 
							
								    *pdwAdviseCookie = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    HRESULT hr;
							 | 
						||
| 
								 | 
							
								    if (StartTime > 0 && PeriodTime > 0 && StartTime != MAX_TIME )
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        *pdwAdviseCookie = m_pSchedule->AddAdvisePacket( StartTime, PeriodTime, HANDLE(hSemaphore), TRUE );
							 | 
						||
| 
								 | 
							
								        hr = *pdwAdviseCookie ? NOERROR : E_OUTOFMEMORY;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else hr = E_INVALIDARG;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return hr;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseReferenceClock::Unadvise(DWORD_PTR dwAdviseCookie)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return m_pSchedule->Unadvise(dwAdviseCookie);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								REFERENCE_TIME CBaseReferenceClock::GetPrivateTime()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CAutoLock cObjectLock(this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /* If the clock has wrapped then the current time will be less than
							 | 
						||
| 
								 | 
							
								     * the last time we were notified so add on the extra milliseconds
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * The time period is long enough so that the likelihood of
							 | 
						||
| 
								 | 
							
								     * successive calls spanning the clock cycle is not considered.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    DWORD dwTime = timeGetTime();
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        m_rtPrivateTime += Int32x32To64(UNITS / MILLISECONDS, (DWORD)(dwTime - m_dwPrevSystemTime));
							 | 
						||
| 
								 | 
							
								        m_dwPrevSystemTime = dwTime;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return m_rtPrivateTime;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Adjust the current time by the input value.  This allows an
							 | 
						||
| 
								 | 
							
								   external time source to work out some of the latency of the clock
							 | 
						||
| 
								 | 
							
								   system and adjust the "current" time accordingly.  The intent is
							 | 
						||
| 
								 | 
							
								   that the time returned to the user is synchronised to a clock
							 | 
						||
| 
								 | 
							
								   source and allows drift to be catered for.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   For example: if the clock source detects a drift it can pass a delta
							 | 
						||
| 
								 | 
							
								   to the current time rather than having to set an explicit time.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseReferenceClock::SetTimeDelta(const REFERENCE_TIME & TimeDelta)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#ifdef DEBUG
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Just break if passed an improper time delta value
							 | 
						||
| 
								 | 
							
								    LONGLONG llDelta = TimeDelta > 0 ? TimeDelta : -TimeDelta;
							 | 
						||
| 
								 | 
							
								    if (llDelta > UNITS * 1000) {
							 | 
						||
| 
								 | 
							
								        DbgLog((LOG_TRACE, 0, TEXT("Bad Time Delta")));
							 | 
						||
| 
								 | 
							
								        //DebugBreak();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // We're going to calculate a "severity" for the time change. Max -1
							 | 
						||
| 
								 | 
							
								    // min 8.  We'll then use this as the debug logging level for a
							 | 
						||
| 
								 | 
							
								    // debug log message.
							 | 
						||
| 
								 | 
							
								    const LONG usDelta = LONG(TimeDelta/10);      // Delta in micro-secs
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    DWORD delta        = abs(usDelta);            // varying delta
							 | 
						||
| 
								 | 
							
								    // Severity == 8 - ceil(log<base 8>(abs( micro-secs delta)))
							 | 
						||
| 
								 | 
							
								    int   Severity     = 8;
							 | 
						||
| 
								 | 
							
								    while ( delta > 0 )
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        delta >>= 3;                              // div 8
							 | 
						||
| 
								 | 
							
								        Severity--;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Sev == 0 => > 2 second delta!
							 | 
						||
| 
								 | 
							
								    DbgLog((LOG_TIMING, Severity < 0 ? 0 : Severity,
							 | 
						||
| 
								 | 
							
								        TEXT("Sev %2i: CSystemClock::SetTimeDelta(%8ld us) %lu -> %lu ms."),
							 | 
						||
| 
								 | 
							
								        Severity, usDelta, DWORD(ConvertToMilliseconds(m_rtPrivateTime)),
							 | 
						||
| 
								 | 
							
								        DWORD(ConvertToMilliseconds(TimeDelta+m_rtPrivateTime)) ));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Don't want the DbgBreak to fire when running stress on debug-builds.
							 | 
						||
| 
								 | 
							
								    #ifdef BREAK_ON_SEVERE_TIME_DELTA
							 | 
						||
| 
								 | 
							
								        if (Severity < 0)
							 | 
						||
| 
								 | 
							
								            DbgBreakPoint(TEXT("SetTimeDelta > 16 seconds!"),
							 | 
						||
| 
								 | 
							
								                          TEXT(__FILE__),__LINE__);
							 | 
						||
| 
								 | 
							
								    #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CAutoLock cObjectLock(this);
							 | 
						||
| 
								 | 
							
								    m_rtPrivateTime += TimeDelta;
							 | 
						||
| 
								 | 
							
								    // If time goes forwards, and we have advises, then we need to
							 | 
						||
| 
								 | 
							
								    // trigger the thread so that it can re-evaluate its wait time.
							 | 
						||
| 
								 | 
							
								    // Since we don't want the cost of the thread switches if the change
							 | 
						||
| 
								 | 
							
								    // is really small, only do it if clock goes forward by more than
							 | 
						||
| 
								 | 
							
								    // 0.5 millisecond.  If the time goes backwards, the thread will
							 | 
						||
| 
								 | 
							
								    // wake up "early" (relativly speaking) and will re-evaluate at
							 | 
						||
| 
								 | 
							
								    // that time.
							 | 
						||
| 
								 | 
							
								    if ( TimeDelta > 5000 && m_pSchedule->GetAdviseCount() > 0 ) TriggerThread();
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Thread stuff
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DWORD __stdcall CBaseReferenceClock::AdviseThreadFunction(__in LPVOID p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return DWORD(reinterpret_cast<CBaseReferenceClock*>(p)->AdviseThread());
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseReferenceClock::AdviseThread()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    DWORD dwWait = INFINITE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // The first thing we do is wait until something interesting happens
							 | 
						||
| 
								 | 
							
								    // (meaning a first advise or shutdown).  This prevents us calling
							 | 
						||
| 
								 | 
							
								    // GetPrivateTime immediately which is goodness as that is a virtual
							 | 
						||
| 
								 | 
							
								    // routine and the derived class may not yet be constructed.  (This
							 | 
						||
| 
								 | 
							
								    // thread is created in the base class constructor.)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while ( !m_bAbort )
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // Wait for an interesting event to happen
							 | 
						||
| 
								 | 
							
								        DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Delay: %lu ms"), dwWait ));
							 | 
						||
| 
								 | 
							
								        WaitForSingleObject(m_pSchedule->GetEvent(), dwWait);
							 | 
						||
| 
								 | 
							
								        if (m_bAbort) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // There are several reasons why we need to work from the internal
							 | 
						||
| 
								 | 
							
								        // time, mainly to do with what happens when time goes backwards.
							 | 
						||
| 
								 | 
							
								        // Mainly, it stop us looping madly if an event is just about to
							 | 
						||
| 
								 | 
							
								        // expire when the clock goes backward (i.e. GetTime stop for a
							 | 
						||
| 
								 | 
							
								        // while).
							 | 
						||
| 
								 | 
							
								        const REFERENCE_TIME  rtNow = GetPrivateTime();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        DbgLog((LOG_TIMING, 3,
							 | 
						||
| 
								 | 
							
								              TEXT("CBaseRefClock::AdviseThread() Woke at = %lu ms"),
							 | 
						||
| 
								 | 
							
								              ConvertToMilliseconds(rtNow) ));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // We must add in a millisecond, since this is the resolution of our
							 | 
						||
| 
								 | 
							
								        // WaitForSingleObject timer.  Failure to do so will cause us to loop
							 | 
						||
| 
								 | 
							
								        // franticly for (approx) 1 a millisecond.
							 | 
						||
| 
								 | 
							
								        m_rtNextAdvise = m_pSchedule->Advise( 10000 + rtNow );
							 | 
						||
| 
								 | 
							
								        LONGLONG llWait = m_rtNextAdvise - rtNow;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ASSERT( llWait > 0 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        llWait = ConvertToMilliseconds(llWait);
							 | 
						||
| 
								 | 
							
								        // DON'T replace this with a max!! (The type's of these things is VERY important)
							 | 
						||
| 
								 | 
							
								        dwWait = (llWait > REFERENCE_TIME(UINT_MAX)) ? UINT_MAX : DWORD(llWait);
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseReferenceClock::SetDefaultTimerResolution(
							 | 
						||
| 
								 | 
							
								        REFERENCE_TIME timerResolution // in 100ns
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CAutoLock cObjectLock(this);
							 | 
						||
| 
								 | 
							
								    if( 0 == timerResolution  ) {
							 | 
						||
| 
								 | 
							
								        if( m_TimerResolution ) {
							 | 
						||
| 
								 | 
							
								           timeEndPeriod( m_TimerResolution );
							 | 
						||
| 
								 | 
							
								           m_TimerResolution = 0;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        TIMECAPS tc;
							 | 
						||
| 
								 | 
							
								        DWORD dwMinResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
							 | 
						||
| 
								 | 
							
								                            ? tc.wPeriodMin
							 | 
						||
| 
								 | 
							
								                            : 1;
							 | 
						||
| 
								 | 
							
								        DWORD dwResolution = max( dwMinResolution, DWORD(timerResolution / 10000) );
							 | 
						||
| 
								 | 
							
								        if( dwResolution != m_TimerResolution ) {
							 | 
						||
| 
								 | 
							
								            timeEndPeriod(m_TimerResolution);
							 | 
						||
| 
								 | 
							
								            m_TimerResolution = dwResolution;
							 | 
						||
| 
								 | 
							
								            timeBeginPeriod( m_TimerResolution );
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return S_OK;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseReferenceClock::GetDefaultTimerResolution(
							 | 
						||
| 
								 | 
							
								        __out REFERENCE_TIME* pTimerResolution // in 100ns
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if( !pTimerResolution ) {
							 | 
						||
| 
								 | 
							
								        return E_POINTER;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    CAutoLock cObjectLock(this);
							 | 
						||
| 
								 | 
							
								    *pTimerResolution = m_TimerResolution * 10000;
							 | 
						||
| 
								 | 
							
								    return S_OK;
							 | 
						||
| 
								 | 
							
								}
							 |