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

📄 renbase.cpp

📁 用DirectX制作高级动画-[Advanced.Animation.with.DirectX]
💻 CPP
📖 第 1 页 / 共 5 页
字号:

// Used to signal end of stream according to the sample end time

void CALLBACK EndOfStreamTimer(UINT uID,        // Timer identifier
                               UINT uMsg,       // Not currently used
                               DWORD_PTR dwUser,// User information
                               DWORD_PTR dw1,   // Windows reserved
                               DWORD_PTR dw2)   // is also reserved
{
    CBaseRenderer *pRenderer = (CBaseRenderer *) dwUser;
    NOTE1("EndOfStreamTimer called (%d)",uID);
    pRenderer->TimerCallback();
}

//  Do the timer callback work
void CBaseRenderer::TimerCallback()
{
    //  Lock for synchronization (but don't hold this lock when calling
    //  timeKillEvent)
    CAutoLock cRendererLock(&m_RendererLock);

    // See if we should signal end of stream now

    if (m_EndOfStreamTimer) {
        m_EndOfStreamTimer = 0;
        SendEndOfStream();
    }
}


// If we are at the end of the stream signal the filter graph but do not set
// the state flag back to FALSE. Once we drop off the end of the stream we
// leave the flag set (until a subsequent ResetEndOfStream). Each sample we
// get delivered will update m_SignalTime to be the last sample's end time.
// We must wait this long before signalling end of stream to the filtergraph

#define TIMEOUT_DELIVERYWAIT 50
#define TIMEOUT_RESOLUTION 10

HRESULT CBaseRenderer::SendEndOfStream()
{
    ASSERT(CritCheckIn(&m_RendererLock));
    if (m_bEOS == FALSE || m_bEOSDelivered || m_EndOfStreamTimer) {
        return NOERROR;
    }

    // If there is no clock then signal immediately
    if (m_pClock == NULL) {
        return NotifyEndOfStream();
    }

    // How long into the future is the delivery time

    REFERENCE_TIME Signal = m_tStart + m_SignalTime;
    REFERENCE_TIME CurrentTime;
    m_pClock->GetTime(&CurrentTime);
    LONG Delay = LONG((Signal - CurrentTime) / 10000);

    // Dump the timing information to the debugger

    NOTE1("Delay until end of stream delivery %d",Delay);
    NOTE1("Current %s",(LPCTSTR)CDisp((LONGLONG)CurrentTime));
    NOTE1("Signal %s",(LPCTSTR)CDisp((LONGLONG)Signal));

    // Wait for the delivery time to arrive

    if (Delay < TIMEOUT_DELIVERYWAIT) {
        return NotifyEndOfStream();
    }

    // Signal a timer callback on another worker thread

    m_EndOfStreamTimer = CompatibleTimeSetEvent((UINT) Delay, // Period of timer
                                      TIMEOUT_RESOLUTION,     // Timer resolution
                                      EndOfStreamTimer,       // Callback function
                                      DWORD_PTR(this),        // Used information
                                      TIME_ONESHOT);          // Type of callback
    if (m_EndOfStreamTimer == 0) {
        return NotifyEndOfStream();
    }
    return NOERROR;
}


// Signals EC_COMPLETE to the filtergraph manager

HRESULT CBaseRenderer::NotifyEndOfStream()
{
    CAutoLock cRendererLock(&m_RendererLock);
    ASSERT(m_bEOSDelivered == FALSE);
    ASSERT(m_EndOfStreamTimer == 0);

    // Has the filter changed state

    if (m_bStreaming == FALSE) {
        ASSERT(m_EndOfStreamTimer == 0);
        return NOERROR;
    }

    // Reset the end of stream timer
    m_EndOfStreamTimer = 0;

    // If we've been using the IMediaPosition interface, set it's start
    // and end media "times" to the stop position by hand.  This ensures
    // that we actually get to the end, even if the MPEG guestimate has
    // been bad or if the quality management dropped the last few frames

    if (m_pPosition) m_pPosition->EOS();
    m_bEOSDelivered = TRUE;
    NOTE("Sending EC_COMPLETE...");
    return NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
}


// Reset the end of stream flag, this is typically called when we transfer to
// stopped states since that resets the current position back to the start so
// we will receive more samples or another EndOfStream if there aren't any. We
// keep two separate flags one to say we have run off the end of the stream
// (this is the m_bEOS flag) and another to say we have delivered EC_COMPLETE
// to the filter graph. We need the latter otherwise we can end up sending an
// EC_COMPLETE every time the source changes state and calls our EndOfStream

HRESULT CBaseRenderer::ResetEndOfStream()
{
    ResetEndOfStreamTimer();
    CAutoLock cRendererLock(&m_RendererLock);

    m_bEOS = FALSE;
    m_bEOSDelivered = FALSE;
    m_SignalTime = 0;

    return NOERROR;
}


// Kills any outstanding end of stream timer

void CBaseRenderer::ResetEndOfStreamTimer()
{
    ASSERT(CritCheckOut(&m_RendererLock));
    if (m_EndOfStreamTimer) {
        timeKillEvent(m_EndOfStreamTimer);
        m_EndOfStreamTimer = 0;
    }
}


// This is called when we start running so that we can schedule any pending
// image we have with the clock and display any timing information. If we
// don't have any sample but we have queued an EOS flag then we send it. If
// we do have a sample then we wait until that has been rendered before we
// signal the filter graph otherwise we may change state before it's done

HRESULT CBaseRenderer::StartStreaming()
{
    CAutoLock cRendererLock(&m_RendererLock);
    if (m_bStreaming == TRUE) {
        return NOERROR;
    }

    // Reset the streaming times ready for running

    m_bStreaming = TRUE;

    timeBeginPeriod(1);
    OnStartStreaming();

    // There should be no outstanding advise
    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
    ASSERT(CancelNotification() == S_FALSE);

    // If we have an EOS and no data then deliver it now

    if (m_pMediaSample == NULL) {
        return SendEndOfStream();
    }

    // Have the data rendered

    ASSERT(m_pMediaSample);
    if (!ScheduleSample(m_pMediaSample))
        m_RenderEvent.Set();

    return NOERROR;
}


// This is called when we stop streaming so that we can set our internal flag
// indicating we are not now to schedule any more samples arriving. The state
// change methods in the filter implementation take care of cancelling any
// clock advise link we have set up and clearing any pending sample we have

HRESULT CBaseRenderer::StopStreaming()
{
    CAutoLock cRendererLock(&m_RendererLock);
    m_bEOSDelivered = FALSE;

    if (m_bStreaming == TRUE) {
        m_bStreaming = FALSE;
        OnStopStreaming();
        timeEndPeriod(1);
    }
    return NOERROR;
}


// We have a boolean flag that is reset when we have signalled EC_REPAINT to
// the filter graph. We set this when we receive an image so that should any
// conditions arise again we can send another one. By having a flag we ensure
// we don't flood the filter graph with redundant calls. We do not set the
// event when we receive an EndOfStream call since there is no point in us
// sending further EC_REPAINTs. In particular the AutoShowWindow method and
// the DirectDraw object use this method to control the window repainting

void CBaseRenderer::SetRepaintStatus(BOOL bRepaint)
{
    CAutoLock cSampleLock(&m_RendererLock);
    m_bRepaintStatus = bRepaint;
}


// Pass the window handle to the upstream filter

void CBaseRenderer::SendNotifyWindow(IPin *pPin,HWND hwnd)
{
    IMediaEventSink *pSink;

    // Does the pin support IMediaEventSink
    HRESULT hr = pPin->QueryInterface(IID_IMediaEventSink,(void **)&pSink);
    if (SUCCEEDED(hr)) {
        pSink->Notify(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
        pSink->Release();
    }
    NotifyEvent(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
}


// Signal an EC_REPAINT to the filter graph. This can be used to have data
// sent to us. For example when a video window is first displayed it may
// not have an image to display, at which point it signals EC_REPAINT. The
// filtergraph will either pause the graph if stopped or if already paused
// it will call put_CurrentPosition of the current position. Setting the
// current position to itself has the stream flushed and the image resent

#define RLOG(_x_) DbgLog((LOG_TRACE,1,TEXT(_x_)));

void CBaseRenderer::SendRepaint()
{
    CAutoLock cSampleLock(&m_RendererLock);
    ASSERT(m_pInputPin);

    // We should not send repaint notifications when...
    //    - An end of stream has been notified
    //    - Our input pin is being flushed
    //    - The input pin is not connected
    //    - We have aborted a video playback
    //    - There is a repaint already sent

    if (m_bAbort == FALSE) {
        if (m_pInputPin->IsConnected() == TRUE) {
            if (m_pInputPin->IsFlushing() == FALSE) {
                if (IsEndOfStream() == FALSE) {
                    if (m_bRepaintStatus == TRUE) {
                        IPin *pPin = (IPin *) m_pInputPin;
                        NotifyEvent(EC_REPAINT,(LONG_PTR) pPin,0);
                        SetRepaintStatus(FALSE);
                        RLOG("Sending repaint");
                    }
                }
            }
        }
    }
}


// When a video window detects a display change (WM_DISPLAYCHANGE message) it
// can send an EC_DISPLAY_CHANGED event code along with the renderer pin. The
// filtergraph will stop everyone and reconnect our input pin. As we're then
// reconnected we can accept the media type that matches the new display mode
// since we may no longer be able to draw the current image type efficiently

BOOL CBaseRenderer::OnDisplayChange()
{
    // Ignore if we are not connected yet

    CAutoLock cSampleLock(&m_RendererLock);
    if (m_pInputPin->IsConnected() == FALSE) {
        return FALSE;
    }

    RLOG("Notification of EC_DISPLAY_CHANGE");

    // Pass our input pin as parameter on the event

    IPin *pPin = (IPin *) m_pInputPin;
    m_pInputPin->AddRef();
    NotifyEvent(EC_DISPLAY_CHANGED,(LONG_PTR) pPin,0);
    SetAbortSignal(TRUE);
    ClearPendingSample();
    m_pInputPin->Release();

    return TRUE;
}


// Called just before we start drawing.
// Store the current time in m_trRenderStart to allow the rendering time to be
// logged.  Log the time stamp of the sample and how late it is (neg is early)

void CBaseRenderer::OnRenderStart(IMediaSample *pMediaSample)
{
#ifdef PERF
    REFERENCE_TIME trStart, trEnd;
    pMediaSample->GetTime(&trStart, &trEnd);

    MSR_INTEGER(m_idBaseStamp, (int)trStart);     // dump low order 32 bits

    m_pClock->GetTime(&m_trRenderStart);
    MSR_INTEGER(0, (int)m_trRenderStart);
    REFERENCE_TIME trStream;
    trStream = m_trRenderStart-m_tStart;     // convert reftime to stream time
    MSR_INTEGER(0,(int)trStream);

    const int trLate = (int)(trStream - trStart);
    MSR_INTEGER(m_idBaseAccuracy, trLate/10000);  // dump in mSec
#endif

} // OnRenderStart


// Called directly after drawing an image.
// calculate the time spent drawing and log it.

void CBaseRenderer::OnRenderEnd(IMediaSample *pMediaSample)
{
#ifdef PERF
    REFERENCE_TIME trNow;
    m_pClock->GetTime(&trNow);
    MSR_INTEGER(0,(int)trNow);
    int t = (int)((trNow - m_trRenderStart)/10000);   // convert UNITS->msec
    MSR_INTEGER(m_idBaseRenderTime, t);
#endif
} // OnRenderEnd




// Constructor must be passed the base renderer object

CRendererInputPin::CRendererInputPin(CBaseRenderer *pRenderer,
                                     HRESULT *phr,
                                     LPCWSTR pPinName) :
    CBaseInputPin(NAME("Renderer pin"),
                  pRenderer,
                  &pRenderer->m_InterfaceLock,
                  (HRESULT *) phr,
                  pPinName)
{
    m_pRenderer = pRenderer;
    ASSERT(m_pRenderer);
}


// Signals end of data stream on the input pin

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

    // Make sure we're streaming ok

    HRESULT hr = CheckStreaming();
    if (hr != NOERROR) {
        return hr;
    }

    // Pass it onto the renderer

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


// Signals start of flushing on the input pin - we do the final reset end of
// stream with the renderer lock unlocked but with the interface lock locked
// We must do this because we call timeKillEvent, our timer callback method
// has to take the renderer lock to serialise our state. Therefore holding a
// renderer lock when calling timeKillEvent could cause a deadlock condition

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

⌨️ 快捷键说明

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