📄 renbase.cpp
字号:
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 + -