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

📄 renbase.cpp

📁 用DirectX制作高级动画-[Advanced.Animation.with.DirectX]
💻 CPP
📖 第 1 页 / 共 5 页
字号:
//------------------------------------------------------------------------------
// File: RenBase.cpp
//
// Desc: DirectShow base classes.
//
// Copyright (c) 1992-2002 Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------


#include <streams.h>        // DirectShow 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;
}

// Implements the CBaseRenderer class

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

    CBaseFilter(pName,pUnk,&m_InterfaceLock,RenderClass),
    m_evComplete(TRUE),
    m_bAbort(FALSE),
    m_pPosition(NULL),
    m_ThreadSignal(TRUE),
    m_bStreaming(FALSE),
    m_bEOS(FALSE),
    m_bEOSDelivered(FALSE),
    m_pMediaSample(NULL),
    m_dwAdvise(0),
    m_pQSink(NULL),
    m_pInputPin(NULL),
    m_bRepaintStatus(TRUE),
    m_SignalTime(0),
    m_bInReceive(FALSE),
    m_EndOfStreamTimer(0)
{
    Ready();
#ifdef PERF
    m_idBaseStamp = MSR_REGISTER(TEXT("BaseRenderer: sample time stamp"));
    m_idBaseRenderTime = MSR_REGISTER(TEXT("BaseRenderer: draw time (msec)"));
    m_idBaseAccuracy = MSR_REGISTER(TEXT("BaseRenderer: Accuracy (msec)"));
#endif
}


// Delete the dynamically allocated IMediaPosition and IMediaSeeking helper
// object. The object is created when somebody queries us. These are standard
// control interfaces for seeking and setting start/stop positions and rates.
// We will probably also have made an input pin based on CRendererInputPin
// that has to be deleted, it's created when an enumerator calls our GetPin

CBaseRenderer::~CBaseRenderer()
{
    ASSERT(m_bStreaming == FALSE);
    ASSERT(m_EndOfStreamTimer == 0);
    StopStreaming();
    ClearPendingSample();

    // Delete any IMediaPosition implementation

    if (m_pPosition) {
        delete m_pPosition;
        m_pPosition = NULL;
    }

    // Delete any input pin created

    if (m_pInputPin) {
        delete m_pInputPin;
        m_pInputPin = NULL;
    }

    // Release any Quality sink

    ASSERT(m_pQSink == NULL);
}


// This returns the IMediaPosition and IMediaSeeking interfaces

HRESULT CBaseRenderer::GetMediaPositionInterface(REFIID riid,void **ppv)
{
    CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
    if (m_pPosition) {
        return m_pPosition->NonDelegatingQueryInterface(riid,ppv);
    }

    HRESULT hr = NOERROR;

    // Create implementation of this dynamically since sometimes we may
    // never try and do a seek. The helper object implements a position
    // control interface (IMediaPosition) which in fact simply takes the
    // calls normally from the filter graph and passes them upstream

    m_pPosition = new CRendererPosPassThru(NAME("Renderer CPosPassThru"),
                                           CBaseFilter::GetOwner(),
                                           (HRESULT *) &hr,
                                           GetPin(0));
    if (m_pPosition == NULL) {
        return E_OUTOFMEMORY;
    }

    if (FAILED(hr)) {
        delete m_pPosition;
        m_pPosition = NULL;
        return E_NOINTERFACE;
    }
    return GetMediaPositionInterface(riid,ppv);
}


// Overriden to say what interfaces we support and where

STDMETHODIMP CBaseRenderer::NonDelegatingQueryInterface(REFIID riid,void **ppv)
{
    // Do we have this interface

    if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
        return GetMediaPositionInterface(riid,ppv);
    } else {
        return CBaseFilter::NonDelegatingQueryInterface(riid,ppv);
    }
}


// This is called whenever we change states, we have a manual reset event that
// is signalled whenever we don't won't the source filter thread to wait in us
// (such as in a stopped state) and likewise is not signalled whenever it can
// wait (during paused and running) this function sets or resets the thread
// event. The event is used to stop source filter threads waiting in Receive

HRESULT CBaseRenderer::SourceThreadCanWait(BOOL bCanWait)
{
    if (bCanWait == TRUE) {
        m_ThreadSignal.Reset();
    } else {
        m_ThreadSignal.Set();
    }
    return NOERROR;
}


#ifdef DEBUG
// Dump the current renderer state to the debug terminal. The hardest part of
// the renderer is the window where we unlock everything to wait for a clock
// to signal it is time to draw or for the application to cancel everything
// by stopping the filter. If we get things wrong we can leave the thread in
// WaitForRenderTime with no way for it to ever get out and we will deadlock

void CBaseRenderer::DisplayRendererState()
{
    DbgLog((LOG_TIMING, 1, TEXT("\nTimed out in WaitForRenderTime")));

    // No way should this be signalled at this point

    BOOL bSignalled = m_ThreadSignal.Check();
    DbgLog((LOG_TIMING, 1, TEXT("Signal sanity check %d"),bSignalled));

    // Now output the current renderer state variables

    DbgLog((LOG_TIMING, 1, TEXT("Filter state %d"),m_State));

    DbgLog((LOG_TIMING, 1, TEXT("Abort flag %d"),m_bAbort));

    DbgLog((LOG_TIMING, 1, TEXT("Streaming flag %d"),m_bStreaming));

    DbgLog((LOG_TIMING, 1, TEXT("Clock advise link %d"),m_dwAdvise));

    DbgLog((LOG_TIMING, 1, TEXT("Current media sample %x"),m_pMediaSample));

    DbgLog((LOG_TIMING, 1, TEXT("EOS signalled %d"),m_bEOS));

    DbgLog((LOG_TIMING, 1, TEXT("EOS delivered %d"),m_bEOSDelivered));

    DbgLog((LOG_TIMING, 1, TEXT("Repaint status %d"),m_bRepaintStatus));


    // Output the delayed end of stream timer information

    DbgLog((LOG_TIMING, 1, TEXT("End of stream timer %x"),m_EndOfStreamTimer));

    DbgLog((LOG_TIMING, 1, TEXT("Deliver time %s"),CDisp((LONGLONG)m_SignalTime)));


    // Should never timeout during a flushing state

    BOOL bFlushing = m_pInputPin->IsFlushing();
    DbgLog((LOG_TIMING, 1, TEXT("Flushing sanity check %d"),bFlushing));

    // Display the time we were told to start at
    DbgLog((LOG_TIMING, 1, TEXT("Last run time %s"),CDisp((LONGLONG)m_tStart.m_time)));

    // Have we got a reference clock
    if (m_pClock == NULL) return;

    // Get the current time from the wall clock

    CRefTime CurrentTime,StartTime,EndTime;
    m_pClock->GetTime((REFERENCE_TIME*) &CurrentTime);
    CRefTime Offset = CurrentTime - m_tStart;

    // Display the current time from the clock

    DbgLog((LOG_TIMING, 1, TEXT("Clock time %s"),CDisp((LONGLONG)CurrentTime.m_time)));

    DbgLog((LOG_TIMING, 1, TEXT("Time difference %dms"),Offset.Millisecs()));


    // Do we have a sample ready to render
    if (m_pMediaSample == NULL) return;

    m_pMediaSample->GetTime((REFERENCE_TIME*)&StartTime, (REFERENCE_TIME*)&EndTime);
    DbgLog((LOG_TIMING, 1, TEXT("Next sample stream times (Start %d End %d ms)"),
           StartTime.Millisecs(),EndTime.Millisecs()));

    // Calculate how long it is until it is due for rendering
    CRefTime Wait = (m_tStart + StartTime) - CurrentTime;
    DbgLog((LOG_TIMING, 1, TEXT("Wait required %d ms"),Wait.Millisecs()));
}
#endif


// Wait until the clock sets the timer event or we're otherwise signalled. We
// set an arbitrary timeout for this wait and if it fires then we display the
// current renderer state on the debugger. It will often fire if the filter's
// left paused in an application however it may also fire during stress tests
// if the synchronisation with application seeks and state changes is faulty

#define RENDER_TIMEOUT 10000

HRESULT CBaseRenderer::WaitForRenderTime()
{
    HANDLE WaitObjects[] = { m_ThreadSignal, m_RenderEvent };
    DWORD Result = WAIT_TIMEOUT;

    // Wait for either the time to arrive or for us to be stopped

    OnWaitStart();
    while (Result == WAIT_TIMEOUT) {
        Result = WaitForMultipleObjects(2,WaitObjects,FALSE,RENDER_TIMEOUT);

#ifdef DEBUG
        if (Result == WAIT_TIMEOUT) DisplayRendererState();
#endif

    }
    OnWaitEnd();

    // We may have been awoken without the timer firing

    if (Result == WAIT_OBJECT_0) {
        return VFW_E_STATE_CHANGED;
    }

    SignalTimerFired();
    return NOERROR;
}


// Poll waiting for Receive to complete.  This really matters when
// Receive may set the palette and cause window messages
// The problem is that if we don't really wait for a renderer to
// stop processing we can deadlock waiting for a transform which
// is calling the renderer's Receive() method because the transform's
// Stop method doesn't know to process window messages to unblock
// the renderer's Receive processing
void CBaseRenderer::WaitForReceiveToComplete()
{
    for (;;) {
        if (!m_bInReceive) {
            break;
        }

        MSG msg;
        //  Receive all interthread sendmessages
        PeekMessage(&msg, NULL, WM_NULL, WM_NULL, PM_NOREMOVE);

        Sleep(1);
    }

    // If the wakebit for QS_POSTMESSAGE is set, the PeekMessage call
    // above just cleared the changebit which will cause some messaging
    // calls to block (waitMessage, MsgWaitFor...) now.
    // Post a dummy message to set the QS_POSTMESSAGE bit again
    if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
        //  Send dummy message
        PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
    }
}

// A filter can have four discrete states, namely Stopped, Running, Paused,
// Intermediate. We are in an intermediate state if we are currently trying
// to pause but haven't yet got the first sample (or if we have been flushed
// in paused state and therefore still have to wait for a sample to arrive)

// This class contains an event called m_evComplete which is signalled when
// the current state is completed and is not signalled when we are waiting to
// complete the last state transition. As mentioned above the only time we
// use this at the moment is when we wait for a media sample in paused state
// If while we are waiting we receive an end of stream notification from the
// source filter then we know no data is imminent so we can reset the event
// This means that when we transition to paused the source filter must call
// end of stream on us or send us an image otherwise we'll hang indefinately


// Simple internal way of getting the real state

FILTER_STATE CBaseRenderer::GetRealState() {
    return m_State;
}


// The renderer doesn't complete the full transition to paused states until
// it has got one media sample to render. If you ask it for its state while
// it's waiting it will return the state along with VFW_S_STATE_INTERMEDIATE

STDMETHODIMP CBaseRenderer::GetState(DWORD dwMSecs,FILTER_STATE *State)
{
    CheckPointer(State,E_POINTER);

    if (WaitDispatchingMessages(m_evComplete, dwMSecs) == WAIT_TIMEOUT) {
        *State = m_State;
        return VFW_S_STATE_INTERMEDIATE;
    }
    *State = m_State;
    return NOERROR;
}


// If we're pausing and we have no samples we don't complete the transition
// to State_Paused and we return S_FALSE. However if the m_bAbort flag has
// been set then all samples are rejected so there is no point waiting for
// one. If we do have a sample then return NOERROR. We will only ever return
// VFW_S_STATE_INTERMEDIATE from GetState after being paused with no sample
// (calling GetState after either being stopped or Run will NOT return this)

HRESULT CBaseRenderer::CompleteStateChange(FILTER_STATE OldState)
{
    // Allow us to be paused when disconnected

    if (m_pInputPin->IsConnected() == FALSE) {
        Ready();
        return S_OK;
    }

    // Have we run off the end of stream

    if (IsEndOfStream() == TRUE) {
        Ready();
        return S_OK;
    }

    // Make sure we get fresh data after being stopped

    if (HaveCurrentSample() == TRUE) {
        if (OldState != State_Stopped) {
            Ready();
            return S_OK;
        }
    }
    NotReady();
    return S_FALSE;
}


// When we stop the filter the things we do are:-

//      Decommit the allocator being used in the connection
//      Release the source filter if it's waiting in Receive
//      Cancel any advise link we set up with the clock
//      Any end of stream signalled is now obsolete so reset
//      Allow us to be stopped when we are not connected

STDMETHODIMP CBaseRenderer::Stop()
{
    CAutoLock cRendererLock(&m_InterfaceLock);

    // Make sure there really is a state change

    if (m_State == State_Stopped) {
        return NOERROR;
    }

    // Is our input pin connected

⌨️ 快捷键说明

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