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

📄 renqual.cpp

📁 basic class basic classbasic class
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of your Microsoft Windows CE
// Source Alliance Program license form.  If you did not accept the terms of
// such a license, you are not authorized to use this source code.
//
//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//
//--------------------------------------------------------------------------;

#include <streams.h>        // ActiveMovie base class definitions
#include <mmsystem.h>       // Needed for definition of timeGetTime
#include <limits.h>         // Standard data type limit definitions
#include <measure.h>        // Used for time critical log functions

#pragma warning(disable:4355)

//  Helper function for clamping time differences
int inline TimeDiff(REFERENCE_TIME rt)
{
    if (rt < - (50 * UNITS)) {
        return -(50 * UNITS);
    } else
        if (rt > 50 * UNITS) {
            return 50 * UNITS;
        } else return (int)rt;
}



// We do not keep an event object to use when setting up a timer link with
// the clock but are given a pointer to one by the owning object through the
// SetNotificationObject method - this must be initialised before starting
// We can override the default quality management process to have it always
// draw late frames, this is currently done by having the following registry
// key (actually an INI key) called DrawLateFrames set to 1 (default is 0)

const TCHAR AMQUALITY[] = TEXT("ActiveMovie");
const TCHAR DRAWLATEFRAMES[] = TEXT("DrawLateFrames");

CVideoRendererQuality::CVideoRendererQuality() :
m_cFramesDropped(0),
m_cFramesDrawn(0),
m_bSupplierHandlingQuality(FALSE)
{
    m_prevState = State_Stopped;

    ResetStreamingTimes();
    
#ifdef PERF
    m_idTimeStamp       = MSR_REGISTER("Frame time stamp");
    m_idEarliness       = MSR_REGISTER("Earliness fudge");
    m_idTarget          = MSR_REGISTER("Target (mSec)");
    m_idSchLateTime     = MSR_REGISTER("mSec late when scheduled");
    m_idDecision        = MSR_REGISTER("Scheduler decision code");
    m_idQualityRate     = MSR_REGISTER("Quality rate sent");
    m_idQualityTime     = MSR_REGISTER("Quality time sent");
    m_idWaitReal        = MSR_REGISTER("Render wait");
    // m_idWait            = MSR_REGISTER("wait time recorded (msec)");
    m_idFrameAccuracy   = MSR_REGISTER("Frame accuracy (msecs)");
    m_bDrawLateFrames = GetProfileInt(AMQUALITY, DRAWLATEFRAMES, FALSE);
    //m_idSendQuality      = MSR_REGISTER("Processing Quality message");
    
    m_idRenderAvg       = MSR_REGISTER("Render draw time Avg");
    m_idFrameAvg        = MSR_REGISTER("FrameAvg");
    m_idWaitAvg         = MSR_REGISTER("WaitAvg");
    m_idDuration        = MSR_REGISTER("Duration");
    m_idThrottle        = MSR_REGISTER("Audio-video throttle wait");
    // m_idDebug           = MSR_REGISTER("Debug stuff");
#endif // PERF
} // Constructor


// Destructor is just a placeholder

CVideoRendererQuality::~CVideoRendererQuality()
{
}


// The timing functions in this class are called by the window object and by
// the renderer's allocator.
// The windows object calls timing functions as it receives media sample
// images for drawing using GDI.
// The allocator calls timing functions when it starts passing DCI/DirectDraw
// surfaces which are not rendered in the same way; The decompressor writes
// directly to the surface with no separate rendering, so those code paths
// call direct into us.  Since we only ever hand out DCI/DirectDraw surfaces
// when we have allocated one and only one image we know there cannot be any
// conflict between the two.
//
// We use timeGetTime to return the timing counts we use (since it's relative
// performance we are interested in rather than absolute compared to a clock)
// The window object sets the accuracy of the system clock (normally 1ms) by
// calling timeBeginPeriod/timeEndPeriod when it changes streaming states


// Reset all times controlling streaming.
// Set them so that
// 1. Frames will not initially be dropped
// 2. The first frame will definitely be drawn (achieved by saying that there
//    has not ben a frame drawn for a long time).

HRESULT CVideoRendererQuality::ResetStreamingTimes()
{
    m_trLastDraw = -1000;     // set up as first frame since ages (1 sec) ago
    m_tStreamingStart = timeGetTime();
    m_trRenderAvg = 0;
    m_trFrameAvg = -1;        // -1000 fps == "unset"
    m_trDuration = 0;         // 0 - silly value
    m_trRenderLast = 0;
    m_trWaitAvg = 0;
    m_tRenderStart = 0;
    m_iTotAcc = 0;
    m_iSumSqAcc = 0;
    m_iSumSqFrameTime = 0;
    m_trFrame = 0;          // hygeine - not really needed
    m_trLate = 0;           // hygeine - not really needed
    m_iSumFrameTime = 0;
    m_nNormal = 0;
    m_trEarliness = 0;
    m_trTarget = -300000;  // 30mSec early
    m_trThrottle = 0;
    m_trRememberStampForPerf = 0;

    //
    // Just reset the frames drawn/dropped count if the previous state when we stopped streaming
    // was stopped. This prevents resetting them when we go to Run from Paused state.
    //
    if (m_prevState == State_Stopped)
    {
        m_cFramesDrawn = 0;
        m_cFramesDropped = 0;
    }

#ifdef PERF
    m_trRememberFrameForPerf = 0;
#endif
    
    return NOERROR;
} // ResetStreamingTimes


// Reset all times controlling streaming. Note that we're now streaming. We
// don't need to set the rendering event to have the source filter released
// as it is done during the Run processing. When we are run we immediately
// release the source filter thread and draw any image waiting (that image
// may already have been drawn once as a poster frame while we were paused)

HRESULT CVideoRendererQuality::OnStartStreamingQual(FILTER_STATE curState)
{
    ResetStreamingTimes();
    return NOERROR;
} // OnStartStreaming


// Called at end of streaming.  Fixes times for property page report

HRESULT CVideoRendererQuality::OnStopStreamingQual(FILTER_STATE curState)
{
    m_tStreamingStart = timeGetTime()-m_tStreamingStart;
    m_prevState = curState;       // save the state when we were issued a stop streaming (may be either Paused/Stopped)
    return NOERROR;
} // OnStopStreaming


// Called when we start waiting for a rendering event.
// Used to update times spent waiting and not waiting.

void CVideoRendererQuality::OnWaitStartQual()
{
    MSR_START(m_idWaitReal);
} // OnWaitStart


// Called when we are awoken from the wait in the window OR by our allocator
// when it is hanging around until the next sample is due for rendering on a
// DCI/DirectDraw surface. We add the wait time into our rolling average.
// We grab the interface lock so that we're serialised with the application
// thread going through the run code - which in due course ends up calling
// ResetStreaming times - possibly as we run through this section of code

void CVideoRendererQuality::OnWaitEndQual()
{
#ifdef PERF
    MSR_STOP(m_idWaitReal);
    // for a perf build we want to know just exactly how late we REALLY are.
    // even if this means that we have to look at the clock again.
    
    REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.
    // We will be discarding overflows like mad here!
    // This is wrong really because timeGetTime() can wrap but it's
    // only for PERF
    REFERENCE_TIME tr = timeGetTime()*10000;
    trRealStream = tr + m_llTimeOffset;
    trRealStream -= m_tStart;     // convert to stream time (this is a reftime)
    
    if (m_trRememberStampForPerf==0) {
        // This is probably the poster frame at the start, and it is not scheduled
        // in the usual way at all.  Just count it.  The rememberstamp gets set
        // in ShouldDrawSampleNow, so this does bogus frame recording until we
        // actually start playing.
        PreparePerformanceData(0, 0);
    } else {
        int trLate = (int)(trRealStream - m_trRememberStampForPerf);
        int trFrame = (int)(tr - m_trRememberFrameForPerf);
        PreparePerformanceData(trLate, trFrame);
    }
    m_trRememberFrameForPerf = tr;
#endif //PERF
} // OnWaitEnd


// Put data on one side that describes the lateness of the current frame.
// We don't yet know whether it will actually be drawn.  In direct draw mode,
// this decision is up to the filter upstream, and it could change its mind.
// The rules say that if it did draw it must call Receive().  One way or
// another we eventually get into either OnRenderStart or OnDirectRender and
// these both call RecordFrameLateness to update the statistics.

void CVideoRendererQuality::PreparePerformanceData(int trLate, int trFrame)
{
    m_trLate = trLate;
    m_trFrame = trFrame;
} // PreparePerformanceData


// update the statistics:
// m_iTotAcc, m_iSumSqAcc, m_iSumSqFrameTime, m_iSumFrameTime, m_cFramesDrawn
// Note that because the properties page reports using these variables,
// 1. We need to be inside a critical section
// 2. They must all be updated together.  Updating the sums here and the count
// elsewhere can result in imaginary jitter (i.e. attempts to find square roots
// of negative numbers) in the property page code.

void CVideoRendererQuality::RecordFrameLateness(int trLate, int trFrame)
{
    // Record how timely we are.
    int tLate = trLate/10000;
    
    // Best estimate of moment of appearing on the screen is average of
    // start and end draw times.  Here we have only the end time.  This may
    // tend to show us as spuriously late by up to 1/2 frame rate achieved.
    // Decoder probably monitors draw time.  We don't bother.
    MSR_INTEGER( m_idFrameAccuracy, tLate );
    
    // This is a hack - we can get frames that are ridiculously late
    // especially (at start-up) and they sod up the statistics.
    // So ignore things that are more than 1 sec off.
    if (tLate>1000 || tLate<-1000) {
        if (m_cFramesDrawn<=1) {
            tLate = 0;
        } else if (tLate>0) {
            tLate = 1000;
        } else {
            tLate = -1000;
        }
    }
    // The very first frame often has a bogus time, so I'm just
    // not going to count it into the statistics.   ???
    if (m_cFramesDrawn>1) {
        m_iTotAcc += tLate;
        m_iSumSqAcc += (tLate*tLate);
    }
    
    // calculate inter-frame time.  Doesn't make sense for first frame
    // second frame suffers from bogus first frame stamp.
    if (m_cFramesDrawn>2) {
        int tFrame = trFrame/10000;    // convert to mSec else it overflows
        // This is a hack.  It can overflow anyway (a pause can cause
        // a very long inter-frame time) and it overflows at 2**31/10**7
        // or about 215 seconds i.e. 3min 35sec
        if (tFrame>1000||tFrame<0) tFrame = 1000;
        m_iSumSqFrameTime += tFrame*tFrame;
        ASSERT(m_iSumSqFrameTime>=0);
        m_iSumFrameTime += tFrame;
    }
    ++m_cFramesDrawn;
    
} // RecordFrameLateness


void CVideoRendererQuality::ThrottleWait()
{
    if (m_trThrottle>0) {
        int iThrottle = m_trThrottle/10000;    // convert to mSec
        MSR_INTEGER( m_idThrottle, iThrottle);
        DbgLog((LOG_TRACE, 0, TEXT("Throttle %d ms"), iThrottle));
        Sleep(iThrottle);
    } 
} // ThrottleWait


// Whenever a frame is rendered it goes though either OnRenderStart
// or OnDirectRender.  Data that are generated during ShouldDrawSample
// are added to the statistics by calling RecordFrameLateness from both
// these two places.

// Called in place of OnRenderStart..OnRenderEnd
// When a DirectDraw image is drawn
void CVideoRendererQuality::OnDirectRender(IMediaSample *pMediaSample)
{
    int time = 0;
    m_trRenderAvg = 0;
    m_trRenderLast = 5000000;  // If we mode switch, we do NOT want this
    // to inhibit the new average getting going!
    // so we set it to half a second
    // MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
    RecordFrameLateness(m_trLate, m_trFrame);
    ThrottleWait();
} // OnDirectRender


// Called just before we start drawing.  All we do is to get the current clock
// time (from the system) and return.  We have to store the start render time
// in a member variable because it isn't used until we complete the drawing
// The rest is just performance logging.

void CVideoRendererQuality::OnRenderStartQual(IMediaSample *pMediaSample)
{
    RecordFrameLateness(m_trLate, m_trFrame);
    m_tRenderStart = timeGetTime();
} // OnRenderStart


// Called directly after drawing an image.  We calculate the time spent in the
// drawing code and if this doesn't appear to have any odd looking spikes in
// it then we add it to the current average draw time.  Measurement spikes may
// occur if the drawing thread is interrupted and switched to somewhere else.

void CVideoRendererQuality::OnRenderEndQual(IMediaSample *pMediaSample)
{
    // The renderer time can vary erratically if we are interrupted so we do
    // some smoothing to help get more sensible figures out but even that is
    // not enough as figures can go 9,10,9,9,83,9 and we must disregard 83
    
    int tr = (timeGetTime() - m_tRenderStart)*10000;   // convert mSec->UNITS
    if (tr < m_trRenderAvg*2 || tr < 2 * m_trRenderLast) {
        // DO_MOVING_AVG(m_trRenderAvg, tr);
        m_trRenderAvg = (tr + (AVGPERIOD-1)*m_trRenderAvg)/AVGPERIOD;
    }
    m_trRenderLast = tr;
    ThrottleWait();
} // OnRenderEnd


STDMETHODIMP CVideoRendererQuality::NotifyQuality(Quality q)
{
    // NOTE:  We are NOT getting any locks here.  We could be called
    // asynchronously and possibly even on a time critical thread of
    // someone else's - so we do the minumum.  We only set one state
    // variable (an integer) and if that happens to be in the middle
    // of another thread reading it they will just get either the new
    // or the old value.  Locking would achieve no more than this.
    
    // It might be nice to check that we are being called from m_pGraph, but
    // it turns out to be a millisecond or so per throw!
    

⌨️ 快捷键说明

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