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

📄 renqual.cpp

📁 basic class basic classbasic class
💻 CPP
📖 第 1 页 / 共 3 页
字号:
    // This is heuristics, these numbers are aimed at being "what works"
    // rather than anything based on some theory.
    // We use a hyperbola because it's easy to calculate and it includes
    // a panic button asymptote (which we push off just to the left)
    // The throttling fits the following table (roughly)
    // Proportion   Throttle (msec)
    //     >=1000         0
    //        900         3
    //        800         7
    //        700        11
    //        600        17
    //        500        25
    //        400        35
    //        300        50
    //        200        72
    //        125       100
    //        100       112
    //         50       146
    //          0       200
    
    // (some evidence that we could go for a sharper kink - e.g. no throttling
    // until below the 750 mark - might give fractionally more frames on a
    // P60-ish machine).  The easy way to get these coefficients is to use
    // Renbase.xls follow the instructions therein using excel solver.
    
    if (q.Proportion>=1000) { m_trThrottle = 0; }
    else {
        // The DWORD is to make quite sure I get unsigned arithmetic
        // as the constant is between 2**31 and 2**32
        m_trThrottle = -330000 + (388880000/(q.Proportion+167));
    }
    return NOERROR;
} // NotifyQuality


// Theory:
// What a supplier wants to know is "is the frame I'm working on NOW
// going to be late?".
// F1 is the frame at the supplier (as above)
// Tf1 is the due time for F1
// T1 is the time at that point (NOW!)
// Tr1 is the time that f1 WILL actually be rendered
// L1 is the latency of the graph for frame F1 = Tr1-T1
// D1 (for delay) is how late F1 will be beyond its due time i.e.
// D1 = (Tr1-Tf1) which is what the supplier really wants to know.
// Unfortunately Tr1 is in the future and is unknown, so is L1
//
// We could estimate L1 by its value for a previous frame,
// L0 = Tr0-T0 and work off
// D1' = ((T1+L0)-Tf1) = (T1 + (Tr0-T0) -Tf1)
// Rearranging terms:
// D1' = (T1-T0) + (Tr0-Tf1)
//       adding (Tf0-Tf0) and rearranging again:
//     = (T1-T0) + (Tr0-Tf0) + (Tf0-Tf1)
//     = (T1-T0) - (Tf1-Tf0) + (Tr0-Tf0)
// But (Tr0-Tf0) is just D0 - how late frame zero was, and this is the
// Late field in the quality message that we send.
// The other two terms just state what correction should be applied before
// using the lateness of F0 to predict the lateness of F1.
// (T1-T0) says how much time has actually passed (we have lost this much)
// (Tf1-Tf0) says how much time should have passed if we were keeping pace
// (we have gained this much).
//
// Suppliers should therefore work off:
//    Quality.Late + (T1-T0)  - (Tf1-Tf0)
// and see if this is "acceptably late" or even early (i.e. negative).
// They get T1 and T0 by polling the clock, they get Tf1 and Tf0 from
// the time stamps in the frames.  They get Quality.Late from us.
//

HRESULT CVideoRendererQuality::GetQuality(REFERENCE_TIME trLate,
                                          REFERENCE_TIME trRealStream,
                                          Quality *pQ)
{
    Quality q;
    
    // If we are the main user of time, then report this as Flood/Dry.
    // If our suppliers are, then report it as Famine/Glut.
    //
    // We need to take action, but avoid hunting.  Hunting is caused by
    // 1. Taking too much action too soon and overshooting
    // 2. Taking too long to react (so averaging can CAUSE hunting).
    //
    // The reason why we use trLate as well as Wait is to reduce hunting;
    // if the wait time is coming down and about to go into the red, we do
    // NOT want to rely on some average which is only telling is that it used
    // to be OK once.
    
    q.TimeStamp = (REFERENCE_TIME)trRealStream;
    
    if (m_trFrameAvg<0) {
        q.Type = Famine;      // guess
    }
    // Is the greater part of the time taken bltting or something else
    else if (m_trFrameAvg > 2*m_trRenderAvg) {
        q.Type = Famine;                        // mainly other
    } else {
        q.Type = Flood;                         // mainly bltting
    }
    
    q.Proportion = 1000;               // default
    
    if (m_trFrameAvg<0) {
        // leave it alone - we don't know enough
    }
    else if ( trLate> 0 ) {
        // try to catch up over the next second
        // We could be Really, REALLY late, but rendering all the frames
        // anyway, just because it's so cheap.
        
        q.Proportion = 1000 - (int)((trLate)/(UNITS/1000));
        if (q.Proportion<500) {
            q.Proportion = 500;      // don't go daft. (could've been negative!)
        } else {
        }
        
    } else if (  m_trWaitAvg>20000
        && trLate<-20000
        ){
        // Go cautiously faster - aim at 2mSec wait.
        if (m_trWaitAvg>=m_trFrameAvg) {
            // This can happen because of some fudges.
            // The waitAvg is how long we originally planned to wait
            // The frameAvg is more honest.
            // It means that we are spending a LOT of time waiting
            q.Proportion = 2000;    // double.
        } else {
            if (m_trFrameAvg+20000 > m_trWaitAvg) {
                q.Proportion
                    = 1000 * (m_trFrameAvg / (m_trFrameAvg + 20000 - m_trWaitAvg));
            } else {
                // We're apparently spending more than the whole frame time waiting.
                // Assume that the averages are slightly out of kilter, but that we
                // are indeed doing a lot of waiting.  (This leg probably never
                // happens, but the code avoids any potential divide by zero).
                q.Proportion = 2000;
            }
        }
        
        if (q.Proportion>2000) {
            q.Proportion = 2000;    // don't go crazy.
        }
    }
    
    // Tell the supplier how late frames are when they get rendered
    // That's how late we are now.
    // If we are in directdraw mode then the guy upstream can see the drawing
    // times and we'll just report on the start time.  He can figure out any
    // offset to apply.  If we are in DIB Section mode then we will apply an
    // extra offset which is half of our drawing time.  This is usually small
    // but can sometimes be the dominant effect.  For this we will use the
    // average drawing time rather than the last frame.  If the last frame took
    // a long time to draw and made us late, that's already in the lateness
    // figure.  We should not add it in again unless we expect the next frame
    // to be the same.  We don't, we expect the average to be a better shot.
    // In direct draw mode the RenderAvg will be zero.
    
    q.Late = trLate + m_trRenderAvg/2;
    
    // log what we're doing
    MSR_INTEGER(m_idQualityRate, q.Proportion);
    MSR_INTEGER( m_idQualityTime, (int)q.Late / 10000 );

    *pQ = q;

    return S_OK;   
} // GetQuality


HRESULT CVideoRendererQuality::ComputeLateness(REFERENCE_TIME trStart,
                                               REFERENCE_TIME trRealStream,
                                               REFERENCE_TIME *ptrLate)
{   
    ASSERT(ptrLate);
    if (!ptrLate)
        return E_INVALIDARG;
   
    // We lose a bit of time depending on the monitor type waiting for the next
    // screen refresh.  On average this might be about 8mSec - so it will be
    // later than we think when the picture appears.  To compensate a bit
    // we bias the media samples by -8mSec i.e. 80000 UNITs.
    // We don't ever make a stream time negative (call it paranoia)
    if (trStart>=80000) {
        trStart -= 80000;
    }
       
    // We have to wory about two versions of "lateness".  The truth, which we
    // try to work out here and the one measured against m_trTarget which
    // includes long term feedback.  We report statistics against the truth
    // but for operational decisions we work to the target.
    // We use TimeDiff to make sure we get an integer because we
    // may actually be late (or more likely early if there is a big time
    // gap) by a very long time.
    *ptrLate = TimeDiff(trRealStream - trStart);

    return S_OK;
}


// We are called with a valid IMediaSample image to decide whether this is to
// be drawn or not.  There must be a reference clock in operation.
// Return S_OK if it is to be drawn Now (as soon as possible)
// Return S_FALSE if it is to be drawn when it's due
// Return an error if we want to drop it
// m_nNormal=-1 indicates that we dropped the previous frame and so this
// one should be drawn early.  Respect it and update it.
// Use current stream time plus a number of heuristics (detailed below)
// to make the decision

HRESULT CVideoRendererQuality::ShouldDrawSampleNow(IMediaSample *pMediaSample,
                                                   REFERENCE_TIME *ptrStart,
                                                   REFERENCE_TIME *ptrEnd,
                                                   REFERENCE_TIME trRealStream,
                                                   BOOL bSupplierHandlingQuality)
{   
    ASSERT(pMediaSample  &&  ptrStart  &&  ptrEnd);
    if (!pMediaSample  ||  !ptrStart  ||  !ptrEnd)
        return E_INVALIDARG;

    // Don't call us unless there's a clock interface to synchronise with
    
    MSR_INTEGER(m_idTimeStamp, (int)((*ptrStart)>>32));   // high order 32 bits
    MSR_INTEGER(m_idTimeStamp, (int)(*ptrStart));         // low order 32 bits
    
    // We lose a bit of time depending on the monitor type waiting for the next
    // screen refresh.  On average this might be about 8mSec - so it will be
    // later than we think when the picture appears.  To compensate a bit
    // we bias the media samples by -8mSec i.e. 80000 UNITs.
    // We don't ever make a stream time negative (call it paranoia)
    if (*ptrStart>=80000) {
        *ptrStart -= 80000;
        *ptrEnd -= 80000;       // bias stop to to retain valid frame duration
    }
    
    // Cache the time stamp now.  We will want to compare what we did with what
    // we started with (after making the monitor allowance).
    m_trRememberStampForPerf = *ptrStart;
    
    // Get reference times (current and late)
#ifdef PERF
    // While the reference clock is expensive:
    // Remember the offset from timeGetTime and use that.
    // This overflows all over the place, but when we subtract to get
    // differences the overflows all cancel out.
    m_llTimeOffset = trRealStream-timeGetTime()*10000;
#endif
    
    // We have to wory about two versions of "lateness".  The truth, which we
    // try to work out here and the one measured against m_trTarget which
    // includes long term feedback.  We report statistics against the truth
    // but for operational decisions we work to the target.
    // We use TimeDiff to make sure we get an integer because we
    // may actually be late (or more likely early if there is a big time
    // gap) by a very long time.
    const int trTrueLate = TimeDiff(trRealStream - *ptrStart);
    const int trLate = trTrueLate;
    
    MSR_INTEGER(m_idSchLateTime, trTrueLate/10000);
    
    // Note: the filter upstream is allowed to this FAIL meaning "you do it".
    m_bSupplierHandlingQuality = bSupplierHandlingQuality;
    
    // Decision time!  Do we drop, draw when ready or draw immediately?
    
    const int trDuration = (int)(*ptrEnd - *ptrStart);
    {
        // We need to see if the frame rate of the file has just changed.
        // This would make comparing our previous frame rate with the current
        // frame rate silly.  Hang on a moment though.  I've seen files
        // where the frames vary between 33 and 34 mSec so as to average
        // 30fps.  A minor variation like that won't hurt us.
        int t = m_trDuration/32;
        if (  trDuration > m_trDuration+t
            || trDuration < m_trDuration-t
            ) {
            // There's a major variation.  Reset the average frame rate to
            // exactly the current rate to disable decision 9002 for this frame,
            // and remember the new rate.
            m_trFrameAvg = trDuration;
            m_trDuration = trDuration;
        }
    }
    
    MSR_INTEGER(m_idEarliness, m_trEarliness/10000);
    MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
    MSR_INTEGER(m_idFrameAvg, m_trFrameAvg/10000);
    MSR_INTEGER(m_idWaitAvg, m_trWaitAvg/10000);
    MSR_INTEGER(m_idDuration, trDuration/10000);
    
#ifdef PERF
    if (S_OK==pMediaSample->IsDiscontinuity()) {
        MSR_INTEGER(m_idDecision, 9000);
    }
#endif
    
    // Control the graceful slide back from slow to fast machine mode.
    // After a frame drop accept an early frame and set the earliness to here
    // If this frame is already later than the earliness then slide it to here
    // otherwise do the standard slide (reduce by about 12% per frame).
    // Note: earliness is normally NEGATIVE
    BOOL bJustDroppedFrame
        = (  m_bSupplierHandlingQuality
        //  Can't use the pin sample properties because we might
        //  not be in Receive when we call this
        && (S_OK == pMediaSample->IsDiscontinuity())          // he just dropped one
        )
        || (m_nNormal==-1);                          // we just dropped one
    
    
    // Set m_trEarliness (slide back from slow to fast machine mode)
    if (trLate>0) {
        m_trEarliness = 0;   // we are no longer in fast machine mode at all!
    } else if (  (trLate>=m_trEarliness) || bJustDroppedFrame) {
        m_trEarliness = trLate;  // Things have slipped of their own accord
    } else {
        m_trEarliness = m_trEarliness - m_trEarliness/8;  // graceful slide
    }
    
    // prepare the new wait average - but don't pollute the old one until
    // we have finished with it.
    int trWaitAvg;
    {
        // We never mix in a negative wait.  This causes us to believe in fast machines
        // slightly more.
        int trL = trLate<0 ? -trLate : 0;
        trWaitAvg = (trL + m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
    }
    
    
    int trFrame;
    {
        REFERENCE_TIME tr = trRealStream - m_trLastDraw; // Cd be large - 4 min pause!
        if (tr>10000000) {
            tr = 10000000;   // 1 second - arbitrarily.
        }
        trFrame = int(tr);
    }
    
    // We will DRAW this frame IF...
    if (
        // ...the time we are spending drawing is a small fraction of the total
        // observed inter-frame time so that dropping it won't help much.

        // 1/10th is a smaller fraction than 1/3rd, which was here before
        (10*m_trRenderAvg <= m_trFrameAvg)
        
        // ...or our supplier is NOT handling things and the next frame would
        // be less timely than this one or our supplier CLAIMS to be handling
        // things, and is now less than a full FOUR frames late.
        || ( m_bSupplierHandlingQuality
        ? (trLate <= trDuration*4)
        : (trLate+trLate < trDuration)
        )
        
        // ...or we are on average waiting for over eight milliseconds then
        // this may be just a glitch.  Draw it and we'll hope to catch up.
        || (m_trWaitAvg > 80000)
        
        // ...or we haven't drawn an image for over a second.  We will update
        // the display, which stops the video looking hung.
        // Do this regardless of how late this media sample is.
        || ((trRealStream - m_trLastDraw) > UNITS)
        
        ) {
        HRESULT Result;
        

⌨️ 快捷键说明

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