home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / refclock.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-14  |  10.1 KB  |  309 lines

  1. //==========================================================================;
  2. //
  3. //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. //  PURPOSE.
  7. //
  8. //  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11. //  REFCLOCK.CPP
  12. //    Implements the IReferenceClock interface
  13.  
  14. #include <streams.h>
  15. #include <limits.h>
  16.  
  17.  
  18. STDMETHODIMP CBaseReferenceClock::NonDelegatingQueryInterface(
  19.     REFIID riid,
  20.     void ** ppv)
  21. {
  22.     CheckPointer(ppv, E_POINTER);
  23.     HRESULT hr;
  24.  
  25.     if (riid == IID_IReferenceClock) 
  26.     {
  27.         hr = GetInterface((IReferenceClock *) this, ppv);
  28.     }
  29.     else
  30.     {
  31.         hr = CUnknown::NonDelegatingQueryInterface(riid, ppv);
  32.     }
  33.     return hr;
  34. }
  35.  
  36. CBaseReferenceClock::~CBaseReferenceClock()
  37. {
  38.     if (m_TimerResolution) timeEndPeriod(m_TimerResolution);
  39.  
  40.     DumpLinkedList();
  41.  
  42.     if (m_hThread) 
  43.     {
  44.         m_bAbort = TRUE;
  45.         TriggerThread();
  46.         WaitForSingleObject( m_hThread, INFINITE );
  47.         EXECUTE_ASSERT( CloseHandle(m_hThread) );
  48.         m_hThread = 0;
  49.     }
  50. }
  51.  
  52.  
  53. CBaseReferenceClock::CBaseReferenceClock(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *phr) 
  54. : CUnknown( pName, pUnk, phr )
  55. , m_rtLastGotTime(0)
  56. , m_hThread(0)
  57. , m_TimerResolution(0)
  58. {
  59.     // If there has been no error up to now...
  60.     // This would only be the case if CUknown or CCritsec failed to
  61.     // construct themselves in which case we would soon fall over as
  62.     // this is the base clock class, or the lists failed to initialise.
  63.     if (*phr == NOERROR) {
  64.  
  65.     // Set up the highest resolution timer we can manage
  66.     TIMECAPS tc;
  67.     m_TimerResolution = (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)))
  68.                             ? tc.wPeriodMin
  69.                             : 1;
  70.  
  71.     timeBeginPeriod(m_TimerResolution);
  72.  
  73.         /* Initialise our system times - the derived clock should set the right values */
  74.         m_dwPrevSystemTime = timeGetTime();
  75.         m_rtPrivateTime = (UNITS / MILLISECONDS) * m_dwPrevSystemTime;
  76.  
  77.         #ifdef PERF
  78.             m_idGetSystemTime = MSR_REGISTER("CBaseReferenceClock::GetTime");
  79.         #endif
  80.  
  81.         DWORD ThreadID;
  82.         m_bAbort = FALSE;
  83.         m_hThread = CreateThread(NULL,                  // Security attributes
  84.                                  (DWORD) 0,             // Initial stack size
  85.                                  AdviseThreadFunction,  // Thread start address
  86.                                  (LPVOID) this,         // Thread parameter
  87.                                  (DWORD) 0,             // Creation flags
  88.                                  &ThreadID);            // Thread identifier
  89.         ASSERT(m_hThread);
  90.  
  91.         if (m_hThread)  SetThreadPriority( m_hThread, THREAD_PRIORITY_TIME_CRITICAL );
  92.         else            *phr = E_FAIL;
  93.     }
  94. }
  95.  
  96. DWORD CBaseReferenceClock::AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic )
  97. {
  98.     Lock();
  99.     const DWORD Result = CAMSchedule::AddAdvisePacket( time1, time2, h, periodic );
  100.     if (time1 < m_rtNextAdvise) TriggerThread();
  101.     Unlock();
  102.     return Result;
  103. }
  104.  
  105. STDMETHODIMP CBaseReferenceClock::GetTime(REFERENCE_TIME *pTime)
  106. {
  107.     HRESULT hr;
  108.     if (pTime) 
  109.     {
  110.         REFERENCE_TIME rtNow;
  111.         Lock();
  112.         rtNow = GetPrivateTime();
  113.         if (rtNow > m_rtLastGotTime)
  114.         {
  115.             m_rtLastGotTime = rtNow;
  116.             hr = S_OK;
  117.         }
  118.         else
  119.         {
  120.             hr = S_FALSE;
  121.         }
  122.         *pTime = m_rtLastGotTime;
  123.         Unlock();
  124.         MSR_INTEGER(m_idGetSystemTime, LONG((*pTime) / (UNITS/MILLISECONDS)) );
  125.     } 
  126.     else hr = E_POINTER;
  127.     
  128.     return hr;
  129. }
  130.  
  131. /* Ask for an async notification that a time has elapsed */
  132.  
  133. STDMETHODIMP CBaseReferenceClock::AdviseTime(
  134.     REFERENCE_TIME baseTime,         // base reference time
  135.     REFERENCE_TIME streamTime,       // stream offset time
  136.     HEVENT hEvent,                  // advise via this event
  137.     DWORD *pdwAdviseCookie)         // where your cookie goes
  138. {
  139.     HRESULT hr = NOERROR;
  140.  
  141.     CheckPointer(pdwAdviseCookie, E_POINTER);
  142.     *pdwAdviseCookie = 0;
  143.     #ifdef DEBUG
  144.     // Check that the event is not already set
  145.     ASSERT(WAIT_TIMEOUT == WaitForSingleObject(HANDLE(hEvent),0));
  146.     #endif
  147.  
  148.     const REFERENCE_TIME lRefTime = baseTime + streamTime;
  149.     if ( lRefTime <= 0 || lRefTime == MAX_TIME )
  150.     {
  151.         hr = E_INVALIDARG;
  152.     }
  153.     else 
  154.     {
  155.         *pdwAdviseCookie = AddAdvisePacket( lRefTime, 0, HANDLE(hEvent), FALSE );
  156.         if ( 0 == *pdwAdviseCookie  ) hr = E_OUTOFMEMORY;
  157.     }
  158.  
  159.     return hr;
  160. }
  161.  
  162.  
  163. /* Ask for an asynchronous periodic notification that a time has elapsed */
  164.  
  165. STDMETHODIMP CBaseReferenceClock::AdvisePeriodic(
  166.     REFERENCE_TIME StartTime,         // starting at this time
  167.     REFERENCE_TIME PeriodTime,        // time between notifications
  168.     HSEMAPHORE hSemaphore,           // advise via a semaphore
  169.     DWORD *pdwAdviseCookie)          // where your cookie goes
  170. {
  171.     HRESULT hr;
  172.     CheckPointer(pdwAdviseCookie, E_POINTER);
  173.     *pdwAdviseCookie = 0;
  174.  
  175.     if (StartTime > 0 && PeriodTime > 0 && StartTime != MAX_TIME )
  176.     {
  177.         *pdwAdviseCookie = AddAdvisePacket( StartTime, PeriodTime, HANDLE(hSemaphore), TRUE );
  178.         if ( 0 == *pdwAdviseCookie  ) hr = E_OUTOFMEMORY;
  179.     }
  180.     else hr = E_INVALIDARG;
  181.  
  182.     return hr;
  183. }
  184.  
  185.  
  186. STDMETHODIMP CBaseReferenceClock::Unadvise(DWORD dwAdviseCookie)
  187. {
  188.     return CAMSchedule::Unadvise(dwAdviseCookie);
  189. }
  190.  
  191.  
  192. REFERENCE_TIME CBaseReferenceClock::GetPrivateTime()
  193. {
  194.     CAutoLock cObjectLock(this);
  195.  
  196.     /* If the clock has wrapped then the current time will be less than
  197.      * the last time we were notified so add on the extra milliseconds
  198.      *
  199.      * The time period is long enough so that the likelihood of
  200.      * successive calls spanning the clock cycle is not considered.
  201.      */
  202.  
  203.     DWORD dwTime = timeGetTime();
  204.     {
  205.         m_rtPrivateTime += (UNITS / MILLISECONDS) * (REFERENCE_TIME(dwTime) - REFERENCE_TIME(m_dwPrevSystemTime));
  206.         if (dwTime < m_dwPrevSystemTime)
  207.             m_rtPrivateTime += (UNITS / MILLISECONDS) * (REFERENCE_TIME(UINT_MAX) + 1);
  208.         m_dwPrevSystemTime = dwTime;
  209.     }
  210.  
  211.     return m_rtPrivateTime;
  212. }
  213.  
  214.  
  215. /* Adjust the current time by the input value.  This allows an
  216.    external time source to work out some of the latency of the clock
  217.    system and adjust the "current" time accordingly.  The intent is
  218.    that the time returned to the user is synchronised to a clock
  219.    source and allows drift to be catered for.
  220.  
  221.    For example: if the clock source detects a drift it can pass a delta
  222.    to the current time rather than having to set an explicit time.
  223.  */
  224.  
  225. STDMETHODIMP CBaseReferenceClock::SetTimeDelta(const REFERENCE_TIME & TimeDelta)
  226. {
  227. #ifdef DEBUG
  228.  
  229.     // We're going to calculate a "severity" for the time change. Max 0
  230.     // min 8.  We'll then use this as the debug logging level for a 
  231.     // debug log message.
  232.     const LONG usDelta = LONG(TimeDelta/10);      // Delta in micro-secs
  233.  
  234.     DWORD delta        = abs(usDelta);            // varying delta
  235.     // Severity == 8 - ceil(log<base 8>(abs( micro-secs delta)))
  236.     int   Severity     = 8;
  237.     while ( delta > 0 )
  238.     {
  239.         delta >>= 3;                              // div 8
  240.         Severity--;
  241.     }
  242.     // So, Severity == 0 => TimeDelta > 2 sec
  243.     ASSERT( Severity > -1 );                      // => >16 sec delta!
  244.  
  245.     DbgLog((LOG_TIMING, Severity < 0 ? 0 : Severity, 
  246.         TEXT("Sev %2i: CSystemClock::SetTimeDelta(%8ld us) %lu -> %lu ms."),
  247.     Severity, usDelta, DWORD(ConvertToMilliseconds(m_rtPrivateTime)), 
  248.         DWORD(ConvertToMilliseconds(TimeDelta+m_rtPrivateTime)) ));
  249. #endif
  250.  
  251.     CAutoLock cObjectLock(this);
  252.     m_rtPrivateTime += TimeDelta;
  253.     // If time goes forwards, and we have advises, then we need to
  254.     // trigger the thread so that it can re-evaluate its wait time.
  255.     // Since we don't want the cost of the thread switches if the change 
  256.     // is really small, only do it if clock goes forward by more than
  257.     // 0.5 millisecond.  If the time goes backwards, the thread will
  258.     // wake up "early" (relativly speaking) and will re-evaluate at 
  259.     // that time.
  260.     if ( TimeDelta > 5000 && GetAdviseCount() > 0 ) TriggerThread();
  261.     return NOERROR;
  262. }
  263.  
  264. // Thread stuff
  265.  
  266. DWORD __stdcall CBaseReferenceClock::AdviseThreadFunction(LPVOID p)
  267. {
  268.     return DWORD(reinterpret_cast<CBaseReferenceClock*>(p)->AdviseThread());
  269. }
  270.  
  271. HRESULT CBaseReferenceClock::AdviseThread()
  272. {
  273.     DWORD           dwWait;
  274.     REFERENCE_TIME  rtNow;
  275.  
  276.     while ( !m_bAbort )
  277.     {
  278.         // There are several reasons why we need to work from the internal
  279.         // time, mainly to do with what happens when time goes backwards.
  280.         // Mainly, it stop us looping madly if an event is just about to
  281.         // expire when the clock goes backward (i.e. GetTime stop for a 
  282.         // while).
  283.         Lock();
  284.         rtNow = GetPrivateTime();
  285.         // We must add in a millisecond, since this is the resolution of our 
  286.         // WaitForSingleObject timer.  Failure to do so will cause us to loop
  287.         // franticly for (approx) 1/2 a millisecond.
  288.         m_rtNextAdvise = Advise( 10000 + rtNow );
  289.         LONGLONG llWait = m_rtNextAdvise - rtNow;
  290.  
  291.         DbgLog((LOG_TIMING, 3, 
  292.               TEXT("CBaseRefClock::AdviseThread() Woke at = %lu ms"),
  293.               ConvertToMilliseconds(rtNow) ));
  294.  
  295.         Unlock();
  296.  
  297.         ASSERT( llWait > 0 );
  298.  
  299.         llWait = ConvertToMilliseconds(llWait);
  300.         // DON'T replace this with a max!! (The type's of these things is VERY important)
  301.         dwWait = (llWait > REFERENCE_TIME(UINT_MAX)) ? UINT_MAX : DWORD(llWait);
  302.         DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Delay: %lu ms"), dwWait ));
  303.  
  304.         WaitForSingleObject(m_Event, dwWait);
  305.     };
  306.     return NOERROR;
  307. }
  308.  
  309.