⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 renqual.cpp

📁 basic class basic classbasic class
💻 CPP
📖 第 1 页 / 共 3 页
字号:
        // We are going to play this frame.  We may want to play it early.
        // We will play it early if we think we are in slow machine mode.
        // If we think we are NOT in slow machine mode, we will still play
        // it early by m_trEarliness as this controls the graceful slide back.
        // and in addition we aim at being m_trTarget late rather than "on time".
        
        BOOL bPlayASAP = FALSE;
        
        // we will play it AT ONCE (slow machine mode) if...
        
        // ...we are playing catch-up
        if ( bJustDroppedFrame) {
            bPlayASAP = TRUE;
            MSR_INTEGER(m_idDecision, 9001);
        }
        
        // ...or if we are running below the true frame rate
        // exact comparisons are glitchy, for these measurements,
        // so add an extra 5% or so
        else if (  (m_trFrameAvg > trDuration + trDuration/16)
            
            // It's possible to get into a state where we are losing ground, but
            // are a very long way ahead.  To avoid this or recover from it
            // we refuse to play early by more than 10 frames.
            && (trLate > - trDuration*10)
            ){
            bPlayASAP = TRUE;
            MSR_INTEGER(m_idDecision, 9002);
        }
#if 0
        // ...or if we have been late and are less than one frame early
        else if (  (trLate + trDuration > 0)
            && (m_trWaitAvg<=20000)
            ) {
            bPlayASAP = TRUE;
            MSR_INTEGER(m_idDecision, 9003);
        }
#endif
        // We will NOT play it at once if we are grossly early.  On very slow frame
        // rate movies - e.g. clock.avi - it is not a good idea to leap ahead just
        // because we got starved (for instance by the net) and dropped one frame
        // some time or other.  If we are more than 900mSec early, then wait.
        if (trLate<-9000000) {
            bPlayASAP = FALSE;
        }
        
        if (bPlayASAP) {
            
            m_nNormal = 0;
            MSR_INTEGER(m_idDecision, 0);
            // When we are here, we are in slow-machine mode.  trLate may well
            // oscillate between negative and positive when the supplier is
            // dropping frames to keep sync.  We should not let that mislead
            // us into thinking that we have as much as zero spare time!
            // We just update with a zero wait.
            m_trWaitAvg = (m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
            
            // Assume that we draw it immediately.  Update inter-frame stats
            m_trFrameAvg = (trFrame + m_trFrameAvg*(AVGPERIOD-1))/AVGPERIOD;
#ifndef PERF
            // if this is NOT a perf build, then report what we know so far
            // without looking at the clock any more.  This assumes that we
            // actually wait for exactly the time we hope to.  it also reports
            // how close we get to the hacked up time stamps that we now have
            // rather than the ones we originally started with.  It will
            // therefore be a little optimistic.  However it's fast.
            PreparePerformanceData(trTrueLate, trFrame);
#endif
            m_trLastDraw = trRealStream;
            if (m_trEarliness > trLate) {
                m_trEarliness = trLate;  // if we are actually early, this is neg
            }
            Result = S_OK;                   // Draw it now
            
        } else {
            ++m_nNormal;
            // Set the average frame rate to EXACTLY the ideal rate.
            // If we are exiting slow-machine mode then we will have caught up
            // and be running ahead, so as we slide back to exact timing we will
            // have a longer than usual gap at this point.  If we record this
            // real gap then we'll think that we're running slow and go back
            // into slow-machine mode and vever get it straight.
            m_trFrameAvg = trDuration;
            MSR_INTEGER(m_idDecision, 1);
            
            // Play it early by m_trEarliness and by m_trTarget
            
            {
                int trE = m_trEarliness;
                if (trE < -m_trFrameAvg) {
                    trE = -m_trFrameAvg;
                }
                *ptrStart += trE;           // N.B. earliness is negative
            }
            
            int Delay = -trTrueLate;
            Result = Delay<=0 ? S_OK : S_FALSE;     // OK = draw now, FALSE = wait
            
            m_trWaitAvg = trWaitAvg;
            
            // Predict when it will actually be drawn and update frame stats
            
            if (Result==S_FALSE) {   // We are going to wait
                trFrame = TimeDiff(*ptrStart-m_trLastDraw);
                m_trLastDraw = *ptrStart;
            } else {
                // trFrame is already = trRealStream-m_trLastDraw;
                m_trLastDraw = trRealStream;
            }
#ifndef PERF
            int iAccuracy;
            if (Delay>0) {
                // Report lateness based on when we intend to play it
                iAccuracy = TimeDiff(*ptrStart-m_trRememberStampForPerf);
            } else {
                // Report lateness based on playing it *now*.
                iAccuracy = trTrueLate;     // trRealStream-RememberStampForPerf;
            }
            PreparePerformanceData(iAccuracy, trFrame);
#endif
        }
        return Result;
    }
    
    // We are going to drop this frame!
    // Of course in DirectDraw mode the guy upstream may draw it anyway.
    
    // This will probably give a large negative wack to the wait avg.
    m_trWaitAvg = trWaitAvg;
    
#ifdef PERF
    // Respect registry setting - debug only!
    if (m_bDrawLateFrames) {
        return S_OK;                        // draw it when it's ready
    }                                      // even though it's late.
#endif
    
    // We are going to drop this frame so draw the next one early
    // n.b. if the supplier is doing direct draw then he may draw it anyway
    // but he's doing something funny to arrive here in that case.
    
    MSR_INTEGER(m_idDecision, 2);
    m_nNormal = -1;
    return E_FAIL;                         // drop it
    
} // ShouldDrawSampleNow


// NOTE we're called by both the window thread and the source filter thread
// so we have to be protected by a critical section (locked before called)
// Also, when the window thread gets signalled to render an image, it always
// does so regardless of how late it is. All the degradation is done when we
// are scheduling the next sample to be drawn. Hence when we start an advise
// link to draw a sample, that sample's time will always become the last one
// drawn - unless of course we stop streaming in which case we cancel links

void CVideoRendererQuality::FrameDropped (IMediaSample *pMediaSample)
{
    ++m_cFramesDropped;
    PERFLOG_TRACE_OBJECT(PERFLOG_EV_RENDERER_OBJECT_DROPPED, PERFLOG_STREAM_VIDEO, -1, pMediaSample, -1, -1, -1, -1, -1);
    
    // m_cFramesDrawn must NOT be updated here.  It has to be updated
    // in RecordFrameLateness at the same time as the other statistics.
}


// Implementation of IQualProp interface needed to support the property page
// This is how the property page gets the data out of the scheduler. We are
// passed into the constructor the owning object in the COM sense, this will
// either be the video renderer or an external IUnknown if we're aggregated.
// We initialise our CUnknown base class with this interface pointer. Then
// all we have to do is to override NonDelegatingQueryInterface to expose
// our IQualProp interface. The AddRef and Release are handled automatically
// by the base class and will be passed on to the appropriate outer object

STDMETHODIMP CVideoRendererQuality::get_FramesDroppedInRenderer(int *pcFramesDropped)
{
    CheckPointer(pcFramesDropped,E_POINTER);
    *pcFramesDropped = m_cFramesDropped;
    return NOERROR;
} // get_FramesDroppedInRenderer


// Set *pcFramesDrawn to the number of frames drawn since
// streaming started.

STDMETHODIMP CVideoRendererQuality::get_FramesDrawn( int *pcFramesDrawn)
{
    CheckPointer(pcFramesDrawn,E_POINTER);
    *pcFramesDrawn = m_cFramesDrawn;
    return NOERROR;
} // get_FramesDrawn


// Set iAvgFrameRate to the frames per hundred secs since
// streaming started.  0 otherwise.

STDMETHODIMP CVideoRendererQuality::get_AvgFrameRate( int *piAvgFrameRate, BOOL bStreaming)
{
    CheckPointer(piAvgFrameRate,E_POINTER);
    
    int t;
    if (bStreaming) {
        t = timeGetTime()-m_tStreamingStart;
    } else {
        t = m_tStreamingStart;
    }
    
    if (t<=0) {
        *piAvgFrameRate = 0;
        ASSERT(m_cFramesDrawn == 0);
    } else if (m_cFramesDrawn < 2) {
        // until we have drawn a few frames the computation
        // of the average frame rate can return wild values
        *piAvgFrameRate = 0;
    } else {
        // i is frames per hundred seconds
        *piAvgFrameRate = MulDiv(100000, m_cFramesDrawn, t);
    }
    return NOERROR;
} // get_AvgFrameRate


// Set *piAvg to the average sync offset since streaming started
// in mSec.  The sync offset is the time in mSec between when the frame
// should have been drawn and when the frame was actually drawn.

STDMETHODIMP CVideoRendererQuality::get_AvgSyncOffset( int *piAvg)
{
    CheckPointer(piAvg,E_POINTER);
    
    // Note that we didn't gather the stats on the first frame
    // so we use m_cFramesDrawn-1 here
    if (m_cFramesDrawn<=1) {
        *piAvg = 0;
    } else {
        *piAvg = (int)(m_iTotAcc / (m_cFramesDrawn-1));
    }
    return NOERROR;
} // get_AvgSyncOffset


// To avoid dragging in the maths library - a cheap
// approximate integer square root.
// We do this by getting a starting guess which is between 1
// and 2 times too large, followed by THREE iterations of
// Newton Raphson.  (That will give accuracy to the nearest mSec
// for the range in question - roughly 0..1000)
//
// It would be faster to use a linear interpolation and ONE NR, but
// who cares.  If anyone does - the best linear interpolation is
// to approximates sqrt(x) by
// y = x * (sqrt(2)-1) + 1 - 1/sqrt(2) + 1/(8*(sqrt(2)-1))
// 0r y = x*0.41421 + 0.59467
// This minimises the maximal error in the range in question.
// (error is about +0.008883 and then one NR will give error .0000something
// (Of course these are integers, so you can't just multiply by 0.41421
// you'd have to do some sort of MulDiv).
// Anyone wanna check my maths?  (This is only for a property display!)

int isqrt(int x)
{
    int s = 1;
    // Make s an initial guess for sqrt(x)
    if (x > 0x40000000) {
        s = 0x8000;     // prevent any conceivable closed loop
    } else {
        while (s*s<x) {    // loop cannot possible go more than 31 times
            s = 2*s;       // normally it goes about 6 times
        }
        // Three NR iterations.
        if (x==0) {
            s= 0; // Wouldn't it be tragic to divide by zero whenever our
            // accuracy was perfect!
        } else {
            s = (s*s+x)/(2*s);
            if (s>=0) s = (s*s+x)/(2*s);
            if (s>=0) s = (s*s+x)/(2*s);
        }
    }
    return s;
}

//
//  Do estimates for standard deviations for per-frame
//  statistics
//
HRESULT CVideoRendererQuality::GetStdDev(
                                      int nSamples,
                                      int *piResult,
                                      LONGLONG llSumSq,
                                      LONGLONG iTot
                                      )
{
    CheckPointer(piResult,E_POINTER);
       
    // If S is the Sum of the Squares of observations and
    //    T the Total (i.e. sum) of the observations and there were
    //    N observations, then an estimate of the standard deviation is
    //      sqrt( (S - T**2/N) / (N-1) )
    
    if (nSamples<=1) {
        *piResult = 0;
    } else {
        LONGLONG x;
        // First frames have bogus stamps, so we get no stats for them
        // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
        
        // so we use m_cFramesDrawn-1 here
        x = llSumSq - llMulDiv(iTot, iTot, nSamples, 0);
        x = x / (nSamples-1);
        ASSERT(x>=0);
        *piResult = isqrt((LONG)x);
    }
    return NOERROR;
}

// Set *piDev to the standard deviation in mSec of the sync offset
// of each frame since streaming started.

STDMETHODIMP CVideoRendererQuality::get_DevSyncOffset( int *piDev)
{
    // First frames have bogus stamps, so we get no stats for them
    // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
    return GetStdDev(m_cFramesDrawn - 1,
        piDev,
        m_iSumSqAcc,
        m_iTotAcc);
} // get_DevSyncOffset


// Set *piJitter to the standard deviation in mSec of the inter-frame time
// of frames since streaming started.

STDMETHODIMP CVideoRendererQuality::get_Jitter( int *piJitter)
{
    // First frames have bogus stamps, so we get no stats for them
    // So second frame gives bogus inter-frame time
    // So we need 3 frames to get 1 datum, so N is cFramesDrawn-2
    return GetStdDev(m_cFramesDrawn - 2,
        piJitter,
        m_iSumSqFrameTime,
        m_iSumFrameTime);
} // get_Jitter


// Overidden to return our IQualProp interface

STDMETHODIMP
CVideoRendererQuality::NonDelegatingQueryInterface(REFIID riid,VOID **ppv)
{
    // We return IQualProp and delegate everything else
    
    if (riid == IID_IQualProp) {
        return GetInterface( (IQualProp *)this, ppv);
    } else {
        return E_NOINTERFACE ;
    }
}


// This removes a large number of level 4 warnings from the
// Microsoft compiler which in this case are not very useful
#pragma warning(disable: 4514)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -