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