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

📄 renbase.cpp

📁 用DirectX制作高级动画-[Advanced.Animation.with.DirectX]
💻 CPP
📖 第 1 页 / 共 5 页
字号:
    ResetEndOfStream();
    ClearPendingSample();
    m_bAbort = FALSE;

    if (State_Running == m_State) {
        StopStreaming();
    }

    return NOERROR;
}


// Retrieves the sample times for this samples (note the sample times are
// passed in by reference not value). We return S_FALSE to say schedule this
// sample according to the times on the sample. We also return S_OK in
// which case the object should simply render the sample data immediately

HRESULT CBaseRenderer::GetSampleTimes(IMediaSample *pMediaSample,
                                      REFERENCE_TIME *pStartTime,
                                      REFERENCE_TIME *pEndTime)
{
    ASSERT(m_dwAdvise == 0);
    ASSERT(pMediaSample);

    // If the stop time for this sample is before or the same as start time,
    // then just ignore it (release it) and schedule the next one in line
    // Source filters should always fill in the start and end times properly!

    if (SUCCEEDED(pMediaSample->GetTime(pStartTime, pEndTime))) {
        if (*pEndTime < *pStartTime) {
            return VFW_E_START_TIME_AFTER_END;
        }
    } else {
        // no time set in the sample... draw it now?
        return S_OK;
    }

    // Can't synchronise without a clock so we return S_OK which tells the
    // caller that the sample should be rendered immediately without going
    // through the overhead of setting a timer advise link with the clock

    if (m_pClock == NULL) {
        return S_OK;
    }
    return ShouldDrawSampleNow(pMediaSample,pStartTime,pEndTime);
}


// By default all samples are drawn according to their time stamps so we
// return S_FALSE. Returning S_OK means draw immediately, this is used
// by the derived video renderer class in its quality management.

HRESULT CBaseRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,
                                           REFERENCE_TIME *ptrStart,
                                           REFERENCE_TIME *ptrEnd)
{
    return S_FALSE;
}


// We must always reset the current advise time to zero after a timer fires
// because there are several possible ways which lead us not to do any more
// scheduling such as the pending image being cleared after state changes

void CBaseRenderer::SignalTimerFired()
{
    m_dwAdvise = 0;
}


// Cancel any notification currently scheduled. This is called by the owning
// window object when it is told to stop streaming. If there is no timer link
// outstanding then calling this is benign otherwise we go ahead and cancel
// We must always reset the render event as the quality management code can
// signal immediate rendering by setting the event without setting an advise
// link. If we're subsequently stopped and run the first attempt to setup an
// advise link with the reference clock will find the event still signalled

HRESULT CBaseRenderer::CancelNotification()
{
    ASSERT(m_dwAdvise == 0 || m_pClock);
    DWORD_PTR dwAdvise = m_dwAdvise;

    // Have we a live advise link

    if (m_dwAdvise) {
        m_pClock->Unadvise(m_dwAdvise);
        SignalTimerFired();
        ASSERT(m_dwAdvise == 0);
    }

    // Clear the event and return our status

    m_RenderEvent.Reset();
    return (dwAdvise ? S_OK : S_FALSE);
}


// Responsible for setting up one shot advise links with the clock
// Return FALSE if the sample is to be dropped (not drawn at all)
// Return TRUE if the sample is to be drawn and in this case also
// arrange for m_RenderEvent to be set at the appropriate time

BOOL CBaseRenderer::ScheduleSample(IMediaSample *pMediaSample)
{
    REFERENCE_TIME StartSample, EndSample;

    // Is someone pulling our leg

    if (pMediaSample == NULL) {
        return FALSE;
    }

    // Get the next sample due up for rendering.  If there aren't any ready
    // then GetNextSampleTimes returns an error.  If there is one to be done
    // then it succeeds and yields the sample times. If it is due now then
    // it returns S_OK other if it's to be done when due it returns S_FALSE

    HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample);
    if (FAILED(hr)) {
        return FALSE;
    }

    // If we don't have a reference clock then we cannot set up the advise
    // time so we simply set the event indicating an image to render. This
    // will cause us to run flat out without any timing or synchronisation

    if (hr == S_OK) {
        EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent));
        return TRUE;
    }

    ASSERT(m_dwAdvise == 0);
    ASSERT(m_pClock);
    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));

    // We do have a valid reference clock interface so we can ask it to
    // set an event when the image comes due for rendering. We pass in
    // the reference time we were told to start at and also the current
    // stream time which is the offset from the start reference time

    hr = m_pClock->AdviseTime(
            (REFERENCE_TIME) m_tStart,          // Start run time
            StartSample,                        // Stream time
            (HEVENT)(HANDLE) m_RenderEvent,     // Render notification
            &m_dwAdvise);                       // Advise cookie

    if (SUCCEEDED(hr)) {
        return TRUE;
    }

    // We could not schedule the next sample for rendering despite the fact
    // we have a valid sample here. This is a fair indication that either
    // the system clock is wrong or the time stamp for the sample is duff

    ASSERT(m_dwAdvise == 0);
    return FALSE;
}


// This is called when a sample comes due for rendering. We pass the sample
// on to the derived class. After rendering we will initialise the timer for
// the next sample, NOTE signal that the last one fired first, if we don't
// do this it thinks there is still one outstanding that hasn't completed

HRESULT CBaseRenderer::Render(IMediaSample *pMediaSample)
{
    // If the media sample is NULL then we will have been notified by the
    // clock that another sample is ready but in the mean time someone has
    // stopped us streaming which causes the next sample to be released

    if (pMediaSample == NULL) {
        return S_FALSE;
    }

    // If we have stopped streaming then don't render any more samples, the
    // thread that got in and locked us and then reset this flag does not
    // clear the pending sample as we can use it to refresh any output device

    if (m_bStreaming == FALSE) {
        return S_FALSE;
    }

    // Time how long the rendering takes

    OnRenderStart(pMediaSample);
    DoRenderSample(pMediaSample);
    OnRenderEnd(pMediaSample);

    return NOERROR;
}


// Checks if there is a sample waiting at the renderer

BOOL CBaseRenderer::HaveCurrentSample()
{
    CAutoLock cRendererLock(&m_RendererLock);
    return (m_pMediaSample == NULL ? FALSE : TRUE);
}


// Returns the current sample waiting at the video renderer. We AddRef the
// sample before returning so that should it come due for rendering the
// person who called this method will hold the remaining reference count
// that will stop the sample being added back onto the allocator free list

IMediaSample *CBaseRenderer::GetCurrentSample()
{
    CAutoLock cRendererLock(&m_RendererLock);
    if (m_pMediaSample) {
        m_pMediaSample->AddRef();
    }
    return m_pMediaSample;
}


// Called when the source delivers us a sample. We go through a few checks to
// make sure the sample can be rendered. If we are running (streaming) then we
// have the sample scheduled with the reference clock, if we are not streaming
// then we have received an sample in paused mode so we can complete any state
// transition. On leaving this function everything will be unlocked so an app
// thread may get in and change our state to stopped (for example) in which
// case it will also signal the thread event so that our wait call is stopped

HRESULT CBaseRenderer::PrepareReceive(IMediaSample *pMediaSample)
{
    CAutoLock cInterfaceLock(&m_InterfaceLock);
    m_bInReceive = TRUE;

    // Check our flushing and filter state

    // This function must hold the interface lock because it calls 
    // CBaseInputPin::Receive() and CBaseInputPin::Receive() uses
    // CBasePin::m_bRunTimeError.
    HRESULT hr = m_pInputPin->CBaseInputPin::Receive(pMediaSample);

    if (hr != NOERROR) {
        m_bInReceive = FALSE;
        return E_FAIL;
    }

    // Has the type changed on a media sample. We do all rendering
    // synchronously on the source thread, which has a side effect
    // that only one buffer is ever outstanding. Therefore when we
    // have Receive called we can go ahead and change the format
    // Since the format change can cause a SendMessage we just don't
    // lock
    if (m_pInputPin->SampleProps()->pMediaType) {
        hr = m_pInputPin->SetMediaType(
                (CMediaType *)m_pInputPin->SampleProps()->pMediaType);
        if (FAILED(hr)) {
            m_bInReceive = FALSE;
            return hr;
        }
    }


    CAutoLock cSampleLock(&m_RendererLock);

    ASSERT(IsActive() == TRUE);
    ASSERT(m_pInputPin->IsFlushing() == FALSE);
    ASSERT(m_pInputPin->IsConnected() == TRUE);
    ASSERT(m_pMediaSample == NULL);

    // Return an error if we already have a sample waiting for rendering
    // source pins must serialise the Receive calls - we also check that
    // no data is being sent after the source signalled an end of stream

    if (m_pMediaSample || m_bEOS || m_bAbort) {
        Ready();
        m_bInReceive = FALSE;
        return E_UNEXPECTED;
    }

    // Store the media times from this sample
    if (m_pPosition) m_pPosition->RegisterMediaTime(pMediaSample);

    // Schedule the next sample if we are streaming

    if ((m_bStreaming == TRUE) && (ScheduleSample(pMediaSample) == FALSE)) {
        ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
        ASSERT(CancelNotification() == S_FALSE);
        m_bInReceive = FALSE;
        return VFW_E_SAMPLE_REJECTED;
    }

    // Store the sample end time for EC_COMPLETE handling
    m_SignalTime = m_pInputPin->SampleProps()->tStop;

    // BEWARE we sometimes keep the sample even after returning the thread to
    // the source filter such as when we go into a stopped state (we keep it
    // to refresh the device with) so we must AddRef it to keep it safely. If
    // we start flushing the source thread is released and any sample waiting
    // will be released otherwise GetBuffer may never return (see BeginFlush)

    m_pMediaSample = pMediaSample;
    m_pMediaSample->AddRef();

    if (m_bStreaming == FALSE) {
        SetRepaintStatus(TRUE);
    }
    return NOERROR;
}


// Called by the source filter when we have a sample to render. Under normal
// circumstances we set an advise link with the clock, wait for the time to
// arrive and then render the data using the PURE virtual DoRenderSample that
// the derived class will have overriden. After rendering the sample we may
// also signal EOS if it was the last one sent before EndOfStream was called

HRESULT CBaseRenderer::Receive(IMediaSample *pSample)
{
    ASSERT(pSample);

    // It may return VFW_E_SAMPLE_REJECTED code to say don't bother

    HRESULT hr = PrepareReceive(pSample);
    ASSERT(m_bInReceive == SUCCEEDED(hr));
    if (FAILED(hr)) {
        if (hr == VFW_E_SAMPLE_REJECTED) {
            return NOERROR;
        }
        return hr;
    }

    // We realize the palette in "PrepareRender()" so we have to give away the
    // filter lock here.
    if (m_State == State_Paused) {
        PrepareRender();
        // no need to use InterlockedExchange
        m_bInReceive = FALSE;
        {
            // We must hold both these locks
            CAutoLock cRendererLock(&m_InterfaceLock);
            if (m_State == State_Stopped)
                return NOERROR;

            m_bInReceive = TRUE;
            CAutoLock cSampleLock(&m_RendererLock);
            OnReceiveFirstSample(pSample);
        }
        Ready();
    }
    // Having set an advise link with the clock we sit and wait. We may be
    // awoken by the clock firing or by a state change. The rendering call
    // will lock the critical section and check we can still render the data

    hr = WaitForRenderTime();
    if (FAILED(hr)) {
        m_bInReceive = FALSE;
        return NOERROR;
    }

    PrepareRender();

    //  Set this here and poll it until we work out the locking correctly
    //  It can't be right that the streaming stuff grabs the interface
    //  lock - after all we want to be able to wait for this stuff
    //  to complete
    m_bInReceive = FALSE;

    // We must hold both these locks
    CAutoLock cRendererLock(&m_InterfaceLock);

    // since we gave away the filter wide lock, the sate of the filter could
    // have chnaged to Stopped
    if (m_State == State_Stopped)
        return NOERROR;

    CAutoLock cSampleLock(&m_RendererLock);

    // Deal with this sample

    Render(m_pMediaSample);
    ClearPendingSample();
    SendEndOfStream();
    CancelNotification();
    return NOERROR;
}


// This is called when we stop or are inactivated to clear the pending sample
// We release the media sample interface so that they can be allocated to the
// source filter again, unless of course we are changing state to inactive in
// which case GetBuffer will return an error. We must also reset the current
// media sample to NULL so that we know we do not currently have an image

HRESULT CBaseRenderer::ClearPendingSample()
{
    CAutoLock cRendererLock(&m_RendererLock);
    if (m_pMediaSample) {
        m_pMediaSample->Release();
        m_pMediaSample = NULL;
    }
    return NOERROR;
}

⌨️ 快捷键说明

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