📄 renbase.cpp
字号:
m_pRenderer->BeginFlush();
}
return m_pRenderer->ResetEndOfStream();
}
// Signals end of flushing on the input pin
STDMETHODIMP CRendererInputPin::EndFlush()
{
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
HRESULT hr = m_pRenderer->EndFlush();
if (SUCCEEDED(hr)) {
hr = CBaseInputPin::EndFlush();
}
return hr;
}
// Pass the sample straight through to the renderer object
STDMETHODIMP CRendererInputPin::Receive(IMediaSample *pSample)
{
HRESULT hr = m_pRenderer->Receive(pSample);
if (FAILED(hr)) {
// A deadlock could occur if the caller holds the renderer lock and
// attempts to acquire the interface lock.
ASSERT(CritCheckOut(&m_pRenderer->m_RendererLock));
{
// The interface lock must be held when the filter is calling
// IsStopped() or IsFlushing(). The interface lock must also
// be held because the function uses m_bRunTimeError.
CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
// We do not report errors which occur while the filter is stopping,
// flushing or if the m_bAbort flag is set . Errors are expected to
// occur during these operations and the streaming thread correctly
// handles the errors.
if (!IsStopped() && !IsFlushing() && !m_pRenderer->m_bAbort && !m_bRunTimeError) {
// EC_ERRORABORT's first parameter is the error which caused
// the event and its' last parameter is 0. See the Direct
// Show SDK documentation for more information.
m_pRenderer->NotifyEvent(EC_ERRORABORT,hr,0);
{
CAutoLock alRendererLock(&m_pRenderer->m_RendererLock);
if (m_pRenderer->IsStreaming() && !m_pRenderer->IsEndOfStreamDelivered()) {
m_pRenderer->NotifyEndOfStream();
}
}
m_bRunTimeError = TRUE;
}
}
}
return hr;
}
// Called when the input pin is disconnected
HRESULT CRendererInputPin::BreakConnect()
{
HRESULT hr = m_pRenderer->BreakConnect();
if (FAILED(hr)) {
return hr;
}
return CBaseInputPin::BreakConnect();
}
// Called when the input pin is connected
HRESULT CRendererInputPin::CompleteConnect(IPin *pReceivePin)
{
HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);
if (FAILED(hr)) {
return hr;
}
return CBaseInputPin::CompleteConnect(pReceivePin);
}
// Give the pin id of our one and only pin
STDMETHODIMP CRendererInputPin::QueryId(LPWSTR *Id)
{
CheckPointer(Id,E_POINTER);
*Id = (LPWSTR)CoTaskMemAlloc(8);
if (*Id == NULL) {
return E_OUTOFMEMORY;
}
lstrcpyW(*Id, L"In");
return NOERROR;
}
// Will the filter accept this media type
HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)
{
return m_pRenderer->CheckMediaType(pmt);
}
// Called when we go paused or running
HRESULT CRendererInputPin::Active()
{
return m_pRenderer->Active();
}
// Called when we go into a stopped state
HRESULT CRendererInputPin::Inactive()
{
// The caller must hold the interface lock because
// this function uses m_bRunTimeError.
ASSERT(CritCheckIn(&m_pRenderer->m_InterfaceLock));
m_bRunTimeError = FALSE;
return m_pRenderer->Inactive();
}
// Tell derived classes about the media type agreed
HRESULT CRendererInputPin::SetMediaType(const CMediaType *pmt)
{
HRESULT hr = CBaseInputPin::SetMediaType(pmt);
if (FAILED(hr)) {
return hr;
}
return m_pRenderer->SetMediaType(pmt);
}
// We do not keep an event object to use when setting up a timer link with
// the clock but are given a pointer to one by the owning object through the
// SetNotificationObject method - this must be initialised before starting
// We can override the default quality management process to have it always
// draw late frames, this is currently done by having the following registry
// key (actually an INI key) called DrawLateFrames set to 1 (default is 0)
const TCHAR AMQUALITY[] = TEXT("ActiveMovie");
const TCHAR DRAWLATEFRAMES[] = TEXT("DrawLateFrames");
CBaseVideoRenderer::CBaseVideoRenderer(
REFCLSID RenderClass, // CLSID for this renderer
TCHAR *pName, // Debug ONLY description
LPUNKNOWN pUnk, // Aggregated owner object
HRESULT *phr) : // General OLE return code
CBaseRenderer(RenderClass,pName,pUnk,phr),
m_cFramesDropped(0),
m_cFramesDrawn(0),
m_bSupplierHandlingQuality(FALSE)
{
ResetStreamingTimes();
#ifdef PERF
m_idTimeStamp = MSR_REGISTER(TEXT("Frame time stamp"));
m_idEarliness = MSR_REGISTER(TEXT("Earliness fudge"));
m_idTarget = MSR_REGISTER(TEXT("Target (mSec)"));
m_idSchLateTime = MSR_REGISTER(TEXT("mSec late when scheduled"));
m_idDecision = MSR_REGISTER(TEXT("Scheduler decision code"));
m_idQualityRate = MSR_REGISTER(TEXT("Quality rate sent"));
m_idQualityTime = MSR_REGISTER(TEXT("Quality time sent"));
m_idWaitReal = MSR_REGISTER(TEXT("Render wait"));
// m_idWait = MSR_REGISTER(TEXT("wait time recorded (msec)"));
m_idFrameAccuracy = MSR_REGISTER(TEXT("Frame accuracy (msecs)"));
m_bDrawLateFrames = GetProfileInt(AMQUALITY, DRAWLATEFRAMES, FALSE);
//m_idSendQuality = MSR_REGISTER(TEXT("Processing Quality message"));
m_idRenderAvg = MSR_REGISTER(TEXT("Render draw time Avg"));
m_idFrameAvg = MSR_REGISTER(TEXT("FrameAvg"));
m_idWaitAvg = MSR_REGISTER(TEXT("WaitAvg"));
m_idDuration = MSR_REGISTER(TEXT("Duration"));
m_idThrottle = MSR_REGISTER(TEXT("Audio-video throttle wait"));
// m_idDebug = MSR_REGISTER(TEXT("Debug stuff"));
#endif // PERF
} // Constructor
// Destructor is just a placeholder
CBaseVideoRenderer::~CBaseVideoRenderer()
{
ASSERT(m_dwAdvise == 0);
}
// The timing functions in this class are called by the window object and by
// the renderer's allocator.
// The windows object calls timing functions as it receives media sample
// images for drawing using GDI.
// The allocator calls timing functions when it starts passing DCI/DirectDraw
// surfaces which are not rendered in the same way; The decompressor writes
// directly to the surface with no separate rendering, so those code paths
// call direct into us. Since we only ever hand out DCI/DirectDraw surfaces
// when we have allocated one and only one image we know there cannot be any
// conflict between the two.
//
// We use timeGetTime to return the timing counts we use (since it's relative
// performance we are interested in rather than absolute compared to a clock)
// The window object sets the accuracy of the system clock (normally 1ms) by
// calling timeBeginPeriod/timeEndPeriod when it changes streaming states
// Reset all times controlling streaming.
// Set them so that
// 1. Frames will not initially be dropped
// 2. The first frame will definitely be drawn (achieved by saying that there
// has not ben a frame drawn for a long time).
HRESULT CBaseVideoRenderer::ResetStreamingTimes()
{
m_trLastDraw = -1000; // set up as first frame since ages (1 sec) ago
m_tStreamingStart = timeGetTime();
m_trRenderAvg = 0;
m_trFrameAvg = -1; // -1000 fps == "unset"
m_trDuration = 0; // 0 - strange value
m_trRenderLast = 0;
m_trWaitAvg = 0;
m_tRenderStart = 0;
m_cFramesDrawn = 0;
m_cFramesDropped = 0;
m_iTotAcc = 0;
m_iSumSqAcc = 0;
m_iSumSqFrameTime = 0;
m_trFrame = 0; // hygiene - not really needed
m_trLate = 0; // hygiene - not really needed
m_iSumFrameTime = 0;
m_nNormal = 0;
m_trEarliness = 0;
m_trTarget = -300000; // 30mSec early
m_trThrottle = 0;
m_trRememberStampForPerf = 0;
#ifdef PERF
m_trRememberFrameForPerf = 0;
#endif
return NOERROR;
} // ResetStreamingTimes
// Reset all times controlling streaming. Note that we're now streaming. We
// don't need to set the rendering event to have the source filter released
// as it is done during the Run processing. When we are run we immediately
// release the source filter thread and draw any image waiting (that image
// may already have been drawn once as a poster frame while we were paused)
HRESULT CBaseVideoRenderer::OnStartStreaming()
{
ResetStreamingTimes();
return NOERROR;
} // OnStartStreaming
// Called at end of streaming. Fixes times for property page report
HRESULT CBaseVideoRenderer::OnStopStreaming()
{
m_tStreamingStart = timeGetTime()-m_tStreamingStart;
return NOERROR;
} // OnStopStreaming
// Called when we start waiting for a rendering event.
// Used to update times spent waiting and not waiting.
void CBaseVideoRenderer::OnWaitStart()
{
MSR_START(m_idWaitReal);
} // OnWaitStart
// Called when we are awoken from the wait in the window OR by our allocator
// when it is hanging around until the next sample is due for rendering on a
// DCI/DirectDraw surface. We add the wait time into our rolling average.
// We grab the interface lock so that we're serialised with the application
// thread going through the run code - which in due course ends up calling
// ResetStreaming times - possibly as we run through this section of code
void CBaseVideoRenderer::OnWaitEnd()
{
#ifdef PERF
MSR_STOP(m_idWaitReal);
// for a perf build we want to know just exactly how late we REALLY are.
// even if this means that we have to look at the clock again.
REFERENCE_TIME trRealStream; // the real time now expressed as stream time.
#if 0
m_pClock->GetTime(&trRealStream); // Calling clock here causes W95 deadlock!
#else
// We will be discarding overflows like mad here!
// This is wrong really because timeGetTime() can wrap but it's
// only for PERF
REFERENCE_TIME tr = timeGetTime()*10000;
trRealStream = tr + m_llTimeOffset;
#endif
trRealStream -= m_tStart; // convert to stream time (this is a reftime)
if (m_trRememberStampForPerf==0) {
// This is probably the poster frame at the start, and it is not scheduled
// in the usual way at all. Just count it. The rememberstamp gets set
// in ShouldDrawSampleNow, so this does invalid frame recording until we
// actually start playing.
PreparePerformanceData(0, 0);
} else {
int trLate = (int)(trRealStream - m_trRememberStampForPerf);
int trFrame = (int)(tr - m_trRememberFrameForPerf);
PreparePerformanceData(trLate, trFrame);
}
m_trRememberFrameForPerf = tr;
#endif //PERF
} // OnWaitEnd
// Put data on one side that describes the lateness of the current frame.
// We don't yet know whether it will actually be drawn. In direct draw mode,
// this decision is up to the filter upstream, and it could change its mind.
// The rules say that if it did draw it must call Receive(). One way or
// another we eventually get into either OnRenderStart or OnDirectRender and
// these both call RecordFrameLateness to update the statistics.
void CBaseVideoRenderer::PreparePerformanceData(int trLate, int trFrame)
{
m_trLate = trLate;
m_trFrame = trFrame;
} // PreparePerformanceData
// update the statistics:
// m_iTotAcc, m_iSumSqAcc, m_iSumSqFrameTime, m_iSumFrameTime, m_cFramesDrawn
// Note that because the properties page reports using these variables,
// 1. We need to be inside a critical section
// 2. They must all be updated together. Updating the sums here and the count
// elsewhere can result in imaginary jitter (i.e. attempts to find square roots
// of negative numbers) in the property page code.
void CBaseVideoRenderer::RecordFrameLateness(int trLate, int trFrame)
{
// Record how timely we are.
int tLate = trLate/10000;
// Best estimate of moment of appearing on the screen is average of
// start and end draw times. Here we have only the end time. This may
// tend to show us as spuriously late by up to 1/2 frame rate achieved.
// Decoder probably monitors draw time. We don't bother.
MSR_INTEGER( m_idFrameAccuracy, tLate );
// This is a kludge - we can get frames that are very late
// especially (at start-up) and they invalidate the statistics.
// So ignore things that are more than 1 sec off.
if (tLate>1000 || tLate<-1000) {
if (m_cFramesDrawn<=1) {
tLate = 0;
} else if (tLate>0) {
tLate = 1000;
} else {
tLate = -1000;
}
}
// The very first frame often has a invalid time, so don't
// count it into the statistics. (???)
if (m_cFramesDrawn>1) {
m_iTotAcc += tLate;
m_iSumSqAcc += (tLate*tLate);
}
// calculate inter-frame time. Doesn't make sense for first frame
// second frame suffers from invalid first frame stamp.
if (m_cFramesDrawn>2) {
int tFrame = trFrame/10000; // convert to mSec else it overflows
// This is a kludge. It can overflow anyway (a pause can cause
// a very long inter-frame time) and it overflows at 2**31/10**7
// or about 215 seconds i.e. 3min 35sec
if (tFrame>1000||tFrame<0) tFrame = 1000;
m_iSumSqFrameTime += tFrame*tFrame;
ASSERT(m_iSumSqFrameTime>=0);
m_iSumFrameTime += tFrame;
}
++m_cFramesDrawn;
} //
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -