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

📄 renbase.cpp

📁 用DirectX制作高级动画-[Advanced.Animation.with.DirectX]
💻 CPP
📖 第 1 页 / 共 5 页
字号:
        m_pRenderer->BeginFlush();
    }
    return m_pRenderer->ResetEndOfStream();
}


// Signals end of flushing on the input pin

STDMETHODIMP CRendererInputPin::EndFlush()
{
    CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
    CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);

    HRESULT hr = m_pRenderer->EndFlush();
    if (SUCCEEDED(hr)) {
        hr = CBaseInputPin::EndFlush();
    }
    return hr;
}


// Pass the sample straight through to the renderer object

STDMETHODIMP CRendererInputPin::Receive(IMediaSample *pSample)
{
    HRESULT hr = m_pRenderer->Receive(pSample);
    if (FAILED(hr)) {

        // A deadlock could occur if the caller holds the renderer lock and
        // attempts to acquire the interface lock.
        ASSERT(CritCheckOut(&m_pRenderer->m_RendererLock));

        {
            // The interface lock must be held when the filter is calling
            // IsStopped() or IsFlushing().  The interface lock must also
            // be held because the function uses m_bRunTimeError.
            CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);

            // We do not report errors which occur while the filter is stopping,
            // flushing or if the m_bAbort flag is set .  Errors are expected to 
            // occur during these operations and the streaming thread correctly 
            // handles the errors.  
            if (!IsStopped() && !IsFlushing() && !m_pRenderer->m_bAbort && !m_bRunTimeError) {

                // EC_ERRORABORT's first parameter is the error which caused
                // the event and its' last parameter is 0.  See the Direct
                // Show SDK documentation for more information.
                m_pRenderer->NotifyEvent(EC_ERRORABORT,hr,0);

                {
                    CAutoLock alRendererLock(&m_pRenderer->m_RendererLock);
                    if (m_pRenderer->IsStreaming() && !m_pRenderer->IsEndOfStreamDelivered()) {
                        m_pRenderer->NotifyEndOfStream();
                    }
                }
    
                m_bRunTimeError = TRUE;
            }
        }
    }

    return hr;
}


// Called when the input pin is disconnected

HRESULT CRendererInputPin::BreakConnect()
{
    HRESULT hr = m_pRenderer->BreakConnect();
    if (FAILED(hr)) {
        return hr;
    }
    return CBaseInputPin::BreakConnect();
}


// Called when the input pin is connected

HRESULT CRendererInputPin::CompleteConnect(IPin *pReceivePin)
{
    HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);
    if (FAILED(hr)) {
        return hr;
    }
    return CBaseInputPin::CompleteConnect(pReceivePin);
}


// Give the pin id of our one and only pin

STDMETHODIMP CRendererInputPin::QueryId(LPWSTR *Id)
{
    CheckPointer(Id,E_POINTER);

    *Id = (LPWSTR)CoTaskMemAlloc(8);
    if (*Id == NULL) {
        return E_OUTOFMEMORY;
    }
    lstrcpyW(*Id, L"In");
    return NOERROR;
}


// Will the filter accept this media type

HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)
{
    return m_pRenderer->CheckMediaType(pmt);
}


// Called when we go paused or running

HRESULT CRendererInputPin::Active()
{
    return m_pRenderer->Active();
}


// Called when we go into a stopped state

HRESULT CRendererInputPin::Inactive()
{
    // The caller must hold the interface lock because 
    // this function uses m_bRunTimeError.
    ASSERT(CritCheckIn(&m_pRenderer->m_InterfaceLock));

    m_bRunTimeError = FALSE;

    return m_pRenderer->Inactive();
}


// Tell derived classes about the media type agreed

HRESULT CRendererInputPin::SetMediaType(const CMediaType *pmt)
{
    HRESULT hr = CBaseInputPin::SetMediaType(pmt);
    if (FAILED(hr)) {
        return hr;
    }
    return m_pRenderer->SetMediaType(pmt);
}


// 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");

CBaseVideoRenderer::CBaseVideoRenderer(
      REFCLSID RenderClass, // CLSID for this renderer
      TCHAR *pName,         // Debug ONLY description
      LPUNKNOWN pUnk,       // Aggregated owner object
      HRESULT *phr) :       // General OLE return code

    CBaseRenderer(RenderClass,pName,pUnk,phr),
    m_cFramesDropped(0),
    m_cFramesDrawn(0),
    m_bSupplierHandlingQuality(FALSE)
{
    ResetStreamingTimes();

#ifdef PERF
    m_idTimeStamp       = MSR_REGISTER(TEXT("Frame time stamp"));
    m_idEarliness       = MSR_REGISTER(TEXT("Earliness fudge"));
    m_idTarget          = MSR_REGISTER(TEXT("Target (mSec)"));
    m_idSchLateTime     = MSR_REGISTER(TEXT("mSec late when scheduled"));
    m_idDecision        = MSR_REGISTER(TEXT("Scheduler decision code"));
    m_idQualityRate     = MSR_REGISTER(TEXT("Quality rate sent"));
    m_idQualityTime     = MSR_REGISTER(TEXT("Quality time sent"));
    m_idWaitReal        = MSR_REGISTER(TEXT("Render wait"));
    // m_idWait            = MSR_REGISTER(TEXT("wait time recorded (msec)"));
    m_idFrameAccuracy   = MSR_REGISTER(TEXT("Frame accuracy (msecs)"));
    m_bDrawLateFrames = GetProfileInt(AMQUALITY, DRAWLATEFRAMES, FALSE);
    //m_idSendQuality      = MSR_REGISTER(TEXT("Processing Quality message"));

    m_idRenderAvg       = MSR_REGISTER(TEXT("Render draw time Avg"));
    m_idFrameAvg        = MSR_REGISTER(TEXT("FrameAvg"));
    m_idWaitAvg         = MSR_REGISTER(TEXT("WaitAvg"));
    m_idDuration        = MSR_REGISTER(TEXT("Duration"));
    m_idThrottle        = MSR_REGISTER(TEXT("Audio-video throttle wait"));
    // m_idDebug           = MSR_REGISTER(TEXT("Debug stuff"));
#endif // PERF
} // Constructor


// Destructor is just a placeholder

CBaseVideoRenderer::~CBaseVideoRenderer()
{
    ASSERT(m_dwAdvise == 0);
}


// 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 CBaseVideoRenderer::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 - strange value
    m_trRenderLast = 0;
    m_trWaitAvg = 0;
    m_tRenderStart = 0;
    m_cFramesDrawn = 0;
    m_cFramesDropped = 0;
    m_iTotAcc = 0;
    m_iSumSqAcc = 0;
    m_iSumSqFrameTime = 0;
    m_trFrame = 0;          // hygiene - not really needed
    m_trLate = 0;           // hygiene - not really needed
    m_iSumFrameTime = 0;
    m_nNormal = 0;
    m_trEarliness = 0;
    m_trTarget = -300000;  // 30mSec early
    m_trThrottle = 0;
    m_trRememberStampForPerf = 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 CBaseVideoRenderer::OnStartStreaming()
{
    ResetStreamingTimes();
    return NOERROR;
} // OnStartStreaming


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

HRESULT CBaseVideoRenderer::OnStopStreaming()
{
    m_tStreamingStart = timeGetTime()-m_tStreamingStart;
    return NOERROR;
} // OnStopStreaming


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

void CBaseVideoRenderer::OnWaitStart()
{
    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 CBaseVideoRenderer::OnWaitEnd()
{
#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.
#if 0
    m_pClock->GetTime(&trRealStream); // Calling clock here causes W95 deadlock!
#else
    // 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;
#endif
    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 invalid 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 CBaseVideoRenderer::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 CBaseVideoRenderer::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 kludge - we can get frames that are very late
    // especially (at start-up) and they invalidate 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 invalid time, so don't
    // 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 invalid first frame stamp.
    if (m_cFramesDrawn>2) {
        int tFrame = trFrame/10000;    // convert to mSec else it overflows

        // This is a kludge.  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;

} // 

⌨️ 快捷键说明

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