2859 lines
		
	
	
		
			97 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			2859 lines
		
	
	
		
			97 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								//------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								// File: RenBase.cpp
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Desc: DirectShow base classes.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Copyright (c) 1992-2001 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
							 | 
						||
| 
								 | 
							
								                             __in_opt LPCTSTR pName,         // Debug ONLY description
							 | 
						||
| 
								 | 
							
								                             __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object
							 | 
						||
| 
								 | 
							
								                             __inout HRESULT *phr) :       // General OLE return code
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CBaseFilter(pName,pUnk,&m_InterfaceLock,RenderClass),
							 | 
						||
| 
								 | 
							
								    m_evComplete(TRUE, phr),
							 | 
						||
| 
								 | 
							
								    m_RenderEvent(FALSE, phr),
							 | 
						||
| 
								 | 
							
								    m_bAbort(FALSE),
							 | 
						||
| 
								 | 
							
								    m_pPosition(NULL),
							 | 
						||
| 
								 | 
							
								    m_ThreadSignal(TRUE, phr),
							 | 
						||
| 
								 | 
							
								    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)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (SUCCEEDED(*phr)) {
							 | 
						||
| 
								 | 
							
								        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, __deref_out void **ppv)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
							 | 
						||
| 
								 | 
							
								    if (m_pPosition) {
							 | 
						||
| 
								 | 
							
								        return m_pPosition->NonDelegatingQueryInterface(riid,ppv);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CBasePin *pPin = GetPin(0);
							 | 
						||
| 
								 | 
							
								    if (NULL == pPin) {
							 | 
						||
| 
								 | 
							
								        return E_OUTOFMEMORY;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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,
							 | 
						||
| 
								 | 
							
								                                           pPin);
							 | 
						||
| 
								 | 
							
								    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, __deref_out 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 snedmessages
							 | 
						||
| 
								 | 
							
								        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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin->IsConnected() == FALSE) {
							 | 
						||
| 
								 | 
							
								        NOTE("Input pin is not connected");
							 | 
						||
| 
								 | 
							
								        m_State = State_Stopped;
							 | 
						||
| 
								 | 
							
								        return NOERROR;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CBaseFilter::Stop();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If we are going into a stopped state then we must decommit whatever
							 | 
						||
| 
								 | 
							
								    // allocator we are using it so that any source filter waiting in the
							 | 
						||
| 
								 | 
							
								    // GetBuffer can be released and unlock themselves for a state change
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin->Allocator()) {
							 | 
						||
| 
								 | 
							
								        m_pInputPin->Allocator()->Decommit();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Cancel any scheduled rendering
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SetRepaintStatus(TRUE);
							 | 
						||
| 
								 | 
							
								    StopStreaming();
							 | 
						||
| 
								 | 
							
								    SourceThreadCanWait(FALSE);
							 | 
						||
| 
								 | 
							
								    ResetEndOfStream();
							 | 
						||
| 
								 | 
							
								    CancelNotification();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // There should be no outstanding clock advise
							 | 
						||
| 
								 | 
							
								    ASSERT(CancelNotification() == S_FALSE);
							 | 
						||
| 
								 | 
							
								    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
							 | 
						||
| 
								 | 
							
								    ASSERT(m_EndOfStreamTimer == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Ready();
							 | 
						||
| 
								 | 
							
								    WaitForReceiveToComplete();
							 | 
						||
| 
								 | 
							
								    m_bAbort = FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// When we pause the filter the things we do are:-
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//      Commit the allocator being used in the connection
							 | 
						||
| 
								 | 
							
								//      Allow a source filter thread to wait in Receive
							 | 
						||
| 
								 | 
							
								//      Cancel any clock advise link (we may be running)
							 | 
						||
| 
								 | 
							
								//      Possibly complete the state change if we have data
							 | 
						||
| 
								 | 
							
								//      Allow us to be paused when we are not connected
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseRenderer::Pause()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CAutoLock cRendererLock(&m_InterfaceLock);
							 | 
						||
| 
								 | 
							
								    FILTER_STATE OldState = m_State;
							 | 
						||
| 
								 | 
							
								    ASSERT(m_pInputPin->IsFlushing() == FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Make sure there really is a state change
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_State == State_Paused) {
							 | 
						||
| 
								 | 
							
								        return CompleteStateChange(State_Paused);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Has our input pin been connected
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin->IsConnected() == FALSE) {
							 | 
						||
| 
								 | 
							
								        NOTE("Input pin is not connected");
							 | 
						||
| 
								 | 
							
								        m_State = State_Paused;
							 | 
						||
| 
								 | 
							
								        return CompleteStateChange(State_Paused);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Pause the base filter class
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    HRESULT hr = CBaseFilter::Pause();
							 | 
						||
| 
								 | 
							
								    if (FAILED(hr)) {
							 | 
						||
| 
								 | 
							
								        NOTE("Pause failed");
							 | 
						||
| 
								 | 
							
								        return hr;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Enable EC_REPAINT events again
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SetRepaintStatus(TRUE);
							 | 
						||
| 
								 | 
							
								    StopStreaming();
							 | 
						||
| 
								 | 
							
								    SourceThreadCanWait(TRUE);
							 | 
						||
| 
								 | 
							
								    CancelNotification();
							 | 
						||
| 
								 | 
							
								    ResetEndOfStreamTimer();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If we are going into a paused state then we must commit whatever
							 | 
						||
| 
								 | 
							
								    // allocator we are using it so that any source filter can call the
							 | 
						||
| 
								 | 
							
								    // GetBuffer and expect to get a buffer without returning an error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin->Allocator()) {
							 | 
						||
| 
								 | 
							
								        m_pInputPin->Allocator()->Commit();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // There should be no outstanding advise
							 | 
						||
| 
								 | 
							
								    ASSERT(CancelNotification() == S_FALSE);
							 | 
						||
| 
								 | 
							
								    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
							 | 
						||
| 
								 | 
							
								    ASSERT(m_EndOfStreamTimer == 0);
							 | 
						||
| 
								 | 
							
								    ASSERT(m_pInputPin->IsFlushing() == FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // When we come out of a stopped state we must clear any image we were
							 | 
						||
| 
								 | 
							
								    // holding onto for frame refreshing. Since renderers see state changes
							 | 
						||
| 
								 | 
							
								    // first we can reset ourselves ready to accept the source thread data
							 | 
						||
| 
								 | 
							
								    // Paused or running after being stopped causes the current position to
							 | 
						||
| 
								 | 
							
								    // be reset so we're not interested in passing end of stream signals
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (OldState == State_Stopped) {
							 | 
						||
| 
								 | 
							
								        m_bAbort = FALSE;
							 | 
						||
| 
								 | 
							
								        ClearPendingSample();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return CompleteStateChange(OldState);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// When we run the filter the things we do are:-
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//      Commit the allocator being used in the connection
							 | 
						||
| 
								 | 
							
								//      Allow a source filter thread to wait in Receive
							 | 
						||
| 
								 | 
							
								//      Signal the render event just to get us going
							 | 
						||
| 
								 | 
							
								//      Start the base class by calling StartStreaming
							 | 
						||
| 
								 | 
							
								//      Allow us to be run when we are not connected
							 | 
						||
| 
								 | 
							
								//      Signal EC_COMPLETE if we are not connected
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseRenderer::Run(REFERENCE_TIME StartTime)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CAutoLock cRendererLock(&m_InterfaceLock);
							 | 
						||
| 
								 | 
							
								    FILTER_STATE OldState = m_State;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Make sure there really is a state change
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_State == State_Running) {
							 | 
						||
| 
								 | 
							
								        return NOERROR;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Send EC_COMPLETE if we're not connected
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin->IsConnected() == FALSE) {
							 | 
						||
| 
								 | 
							
								        NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
							 | 
						||
| 
								 | 
							
								        m_State = State_Running;
							 | 
						||
| 
								 | 
							
								        return NOERROR;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Ready();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Pause the base filter class
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    HRESULT hr = CBaseFilter::Run(StartTime);
							 | 
						||
| 
								 | 
							
								    if (FAILED(hr)) {
							 | 
						||
| 
								 | 
							
								        NOTE("Run failed");
							 | 
						||
| 
								 | 
							
								        return hr;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Allow the source thread to wait
							 | 
						||
| 
								 | 
							
								    ASSERT(m_pInputPin->IsFlushing() == FALSE);
							 | 
						||
| 
								 | 
							
								    SourceThreadCanWait(TRUE);
							 | 
						||
| 
								 | 
							
								    SetRepaintStatus(FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // There should be no outstanding advise
							 | 
						||
| 
								 | 
							
								    ASSERT(CancelNotification() == S_FALSE);
							 | 
						||
| 
								 | 
							
								    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
							 | 
						||
| 
								 | 
							
								    ASSERT(m_EndOfStreamTimer == 0);
							 | 
						||
| 
								 | 
							
								    ASSERT(m_pInputPin->IsFlushing() == FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If we are going into a running state then we must commit whatever
							 | 
						||
| 
								 | 
							
								    // allocator we are using it so that any source filter can call the
							 | 
						||
| 
								 | 
							
								    // GetBuffer and expect to get a buffer without returning an error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin->Allocator()) {
							 | 
						||
| 
								 | 
							
								        m_pInputPin->Allocator()->Commit();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // When we come out of a stopped state we must clear any image we were
							 | 
						||
| 
								 | 
							
								    // holding onto for frame refreshing. Since renderers see state changes
							 | 
						||
| 
								 | 
							
								    // first we can reset ourselves ready to accept the source thread data
							 | 
						||
| 
								 | 
							
								    // Paused or running after being stopped causes the current position to
							 | 
						||
| 
								 | 
							
								    // be reset so we're not interested in passing end of stream signals
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (OldState == State_Stopped) {
							 | 
						||
| 
								 | 
							
								        m_bAbort = FALSE;
							 | 
						||
| 
								 | 
							
								        ClearPendingSample();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return StartStreaming();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Return the number of input pins we support
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int CBaseRenderer::GetPinCount()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin == NULL) {
							 | 
						||
| 
								 | 
							
								        //  Try to create it
							 | 
						||
| 
								 | 
							
								        (void)GetPin(0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return m_pInputPin != NULL ? 1 : 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// We only support one input pin and it is numbered zero
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CBasePin *CBaseRenderer::GetPin(int n)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Should only ever be called with zero
							 | 
						||
| 
								 | 
							
								    ASSERT(n == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (n != 0) {
							 | 
						||
| 
								 | 
							
								        return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Create the input pin if not already done so
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin == NULL) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // hr must be initialized to NOERROR because
							 | 
						||
| 
								 | 
							
								        // CRendererInputPin's constructor only changes
							 | 
						||
| 
								 | 
							
								        // hr's value if an error occurs.
							 | 
						||
| 
								 | 
							
								        HRESULT hr = NOERROR;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        m_pInputPin = new CRendererInputPin(this,&hr,L"In");
							 | 
						||
| 
								 | 
							
								        if (NULL == m_pInputPin) {
							 | 
						||
| 
								 | 
							
								            return NULL;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (FAILED(hr)) {
							 | 
						||
| 
								 | 
							
								            delete m_pInputPin;
							 | 
						||
| 
								 | 
							
								            m_pInputPin = NULL;
							 | 
						||
| 
								 | 
							
								            return NULL;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return m_pInputPin;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// If "In" then return the IPin for our input pin, otherwise NULL and error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseRenderer::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(ppPin,E_POINTER);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (0==lstrcmpW(Id,L"In")) {
							 | 
						||
| 
								 | 
							
								        *ppPin = GetPin(0);
							 | 
						||
| 
								 | 
							
								        if (*ppPin) {
							 | 
						||
| 
								 | 
							
								            (*ppPin)->AddRef();
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            return E_OUTOFMEMORY;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        *ppPin = NULL;
							 | 
						||
| 
								 | 
							
								        return VFW_E_NOT_FOUND;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Called when the input pin receives an EndOfStream notification. If we have
							 | 
						||
| 
								 | 
							
								// not got a sample, then notify EC_COMPLETE now. If we have samples, then set
							 | 
						||
| 
								 | 
							
								// m_bEOS and check for this on completing samples. If we're waiting to pause
							 | 
						||
| 
								 | 
							
								// then complete the transition to paused state by setting the state event
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::EndOfStream()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // Ignore these calls if we are stopped
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_State == State_Stopped) {
							 | 
						||
| 
								 | 
							
								        return NOERROR;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If we have a sample then wait for it to be rendered
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    m_bEOS = TRUE;
							 | 
						||
| 
								 | 
							
								    if (m_pMediaSample) {
							 | 
						||
| 
								 | 
							
								        return NOERROR;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If we are waiting for pause then we are now ready since we cannot now
							 | 
						||
| 
								 | 
							
								    // carry on waiting for a sample to arrive since we are being told there
							 | 
						||
| 
								 | 
							
								    // won't be any. This sets an event that the GetState function picks up
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Ready();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Only signal completion now if we are running otherwise queue it until
							 | 
						||
| 
								 | 
							
								    // we do run in StartStreaming. This is used when we seek because a seek
							 | 
						||
| 
								 | 
							
								    // causes a pause where early notification of completion is misleading
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_bStreaming) {
							 | 
						||
| 
								 | 
							
								        SendEndOfStream();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// When we are told to flush we should release the source thread
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::BeginFlush()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // If paused then report state intermediate until we get some data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_State == State_Paused) {
							 | 
						||
| 
								 | 
							
								        NotReady();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SourceThreadCanWait(FALSE);
							 | 
						||
| 
								 | 
							
								    CancelNotification();
							 | 
						||
| 
								 | 
							
								    ClearPendingSample();
							 | 
						||
| 
								 | 
							
								    //  Wait for Receive to complete
							 | 
						||
| 
								 | 
							
								    WaitForReceiveToComplete();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// After flushing the source thread can wait in Receive again
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::EndFlush()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // Reset the current sample media time
							 | 
						||
| 
								 | 
							
								    if (m_pPosition) m_pPosition->ResetMediaTime();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // There should be no outstanding advise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ASSERT(CancelNotification() == S_FALSE);
							 | 
						||
| 
								 | 
							
								    SourceThreadCanWait(TRUE);
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// We can now send EC_REPAINTs if so required
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::CompleteConnect(IPin *pReceivePin)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // The caller should always hold the interface lock because
							 | 
						||
| 
								 | 
							
								    // the function uses CBaseFilter::m_State.
							 | 
						||
| 
								 | 
							
								    ASSERT(CritCheckIn(&m_InterfaceLock));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    m_bAbort = FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (State_Running == GetRealState()) {
							 | 
						||
| 
								 | 
							
								        HRESULT hr = StartStreaming();
							 | 
						||
| 
								 | 
							
								        if (FAILED(hr)) {
							 | 
						||
| 
								 | 
							
								            return hr;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        SetRepaintStatus(FALSE);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        SetRepaintStatus(TRUE);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Called when we go paused or running
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::Active()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Called when we go into a stopped state
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::Inactive()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (m_pPosition) {
							 | 
						||
| 
								 | 
							
								        m_pPosition->ResetMediaTime();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    //  People who derive from this may want to override this behaviour
							 | 
						||
| 
								 | 
							
								    //  to keep hold of the sample in some circumstances
							 | 
						||
| 
								 | 
							
								    ClearPendingSample();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Tell derived classes about the media type agreed
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::SetMediaType(const CMediaType *pmt)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// When we break the input pin connection we should reset the EOS flags. When
							 | 
						||
| 
								 | 
							
								// we are asked for either IMediaPosition or IMediaSeeking we will create a
							 | 
						||
| 
								 | 
							
								// CPosPassThru object to handles media time pass through. When we're handed
							 | 
						||
| 
								 | 
							
								// samples we store (by calling CPosPassThru::RegisterMediaTime) their media
							 | 
						||
| 
								 | 
							
								// times so we can then return a real current position of data being rendered
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseRenderer::BreakConnect()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // Do we have a quality management sink
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pQSink) {
							 | 
						||
| 
								 | 
							
								        m_pQSink->Release();
							 | 
						||
| 
								 | 
							
								        m_pQSink = NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Check we have a valid connection
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pInputPin->IsConnected() == FALSE) {
							 | 
						||
| 
								 | 
							
								        return S_FALSE;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Check we are stopped before disconnecting
							 | 
						||
| 
								 | 
							
								    if (m_State != State_Stopped && !m_pInputPin->CanReconnectWhenActive()) {
							 | 
						||
| 
								 | 
							
								        return VFW_E_NOT_STOPPED;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SetRepaintStatus(FALSE);
							 | 
						||
| 
								 | 
							
								    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,
							 | 
						||
| 
								 | 
							
								                                      __out REFERENCE_TIME *pStartTime,
							 | 
						||
| 
								 | 
							
								                                      __out 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,
							 | 
						||
| 
								 | 
							
								                                           __out REFERENCE_TIME *ptrStart,
							 | 
						||
| 
								 | 
							
								                                           __out 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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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(__inout CBaseRenderer *pRenderer,
							 | 
						||
| 
								 | 
							
								                                     __inout HRESULT *phr,
							 | 
						||
| 
								 | 
							
								                                     __in_opt 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();
							 | 
						||
| 
								 | 
							
								        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(__deref_out LPWSTR *Id)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(Id,E_POINTER);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const WCHAR szIn[] = L"In";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    *Id = (LPWSTR)CoTaskMemAlloc(sizeof(szIn));
							 | 
						||
| 
								 | 
							
								    if (*Id == NULL) {
							 | 
						||
| 
								 | 
							
								        return E_OUTOFMEMORY;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    CopyMemory(*Id, szIn, sizeof(szIn));
							 | 
						||
| 
								 | 
							
								    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
							 | 
						||
| 
								 | 
							
								      __in_opt LPCTSTR pName,         // Debug ONLY description
							 | 
						||
| 
								 | 
							
								      __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object
							 | 
						||
| 
								 | 
							
								      __inout 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;          // hygeine - not really needed
							 | 
						||
| 
								 | 
							
								    m_trLate = 0;           // hygeine - 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;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								} // RecordFrameLateness
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CBaseVideoRenderer::ThrottleWait()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (m_trThrottle>0) {
							 | 
						||
| 
								 | 
							
								        int iThrottle = m_trThrottle/10000;    // convert to mSec
							 | 
						||
| 
								 | 
							
								        MSR_INTEGER( m_idThrottle, iThrottle);
							 | 
						||
| 
								 | 
							
								        DbgLog((LOG_TRACE, 0, TEXT("Throttle %d ms"), iThrottle));
							 | 
						||
| 
								 | 
							
								        Sleep(iThrottle);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        Sleep(0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								} // ThrottleWait
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Whenever a frame is rendered it goes though either OnRenderStart
							 | 
						||
| 
								 | 
							
								// or OnDirectRender.  Data that are generated during ShouldDrawSample
							 | 
						||
| 
								 | 
							
								// are added to the statistics by calling RecordFrameLateness from both
							 | 
						||
| 
								 | 
							
								// these two places.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Called in place of OnRenderStart..OnRenderEnd
							 | 
						||
| 
								 | 
							
								// When a DirectDraw image is drawn
							 | 
						||
| 
								 | 
							
								void CBaseVideoRenderer::OnDirectRender(IMediaSample *pMediaSample)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    m_trRenderAvg = 0;
							 | 
						||
| 
								 | 
							
								    m_trRenderLast = 5000000;  // If we mode switch, we do NOT want this
							 | 
						||
| 
								 | 
							
								                               // to inhibit the new average getting going!
							 | 
						||
| 
								 | 
							
								                               // so we set it to half a second
							 | 
						||
| 
								 | 
							
								    // MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
							 | 
						||
| 
								 | 
							
								    RecordFrameLateness(m_trLate, m_trFrame);
							 | 
						||
| 
								 | 
							
								    ThrottleWait();
							 | 
						||
| 
								 | 
							
								} // OnDirectRender
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Called just before we start drawing.  All we do is to get the current clock
							 | 
						||
| 
								 | 
							
								// time (from the system) and return.  We have to store the start render time
							 | 
						||
| 
								 | 
							
								// in a member variable because it isn't used until we complete the drawing
							 | 
						||
| 
								 | 
							
								// The rest is just performance logging.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CBaseVideoRenderer::OnRenderStart(IMediaSample *pMediaSample)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    RecordFrameLateness(m_trLate, m_trFrame);
							 | 
						||
| 
								 | 
							
								    m_tRenderStart = timeGetTime();
							 | 
						||
| 
								 | 
							
								} // OnRenderStart
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Called directly after drawing an image.  We calculate the time spent in the
							 | 
						||
| 
								 | 
							
								// drawing code and if this doesn't appear to have any odd looking spikes in
							 | 
						||
| 
								 | 
							
								// it then we add it to the current average draw time.  Measurement spikes may
							 | 
						||
| 
								 | 
							
								// occur if the drawing thread is interrupted and switched to somewhere else.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CBaseVideoRenderer::OnRenderEnd(IMediaSample *pMediaSample)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // The renderer time can vary erratically if we are interrupted so we do
							 | 
						||
| 
								 | 
							
								    // some smoothing to help get more sensible figures out but even that is
							 | 
						||
| 
								 | 
							
								    // not enough as figures can go 9,10,9,9,83,9 and we must disregard 83
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int tr = (timeGetTime() - m_tRenderStart)*10000;   // convert mSec->UNITS
							 | 
						||
| 
								 | 
							
								    if (tr < m_trRenderAvg*2 || tr < 2 * m_trRenderLast) {
							 | 
						||
| 
								 | 
							
								        // DO_MOVING_AVG(m_trRenderAvg, tr);
							 | 
						||
| 
								 | 
							
								        m_trRenderAvg = (tr + (AVGPERIOD-1)*m_trRenderAvg)/AVGPERIOD;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    m_trRenderLast = tr;
							 | 
						||
| 
								 | 
							
								    ThrottleWait();
							 | 
						||
| 
								 | 
							
								} // OnRenderEnd
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::SetSink( IQualityControl * piqc)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    m_pQSink = piqc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								} // SetSink
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::Notify( IBaseFilter * pSelf, Quality q)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // NOTE:  We are NOT getting any locks here.  We could be called
							 | 
						||
| 
								 | 
							
								    // asynchronously and possibly even on a time critical thread of
							 | 
						||
| 
								 | 
							
								    // someone else's - so we do the minumum.  We only set one state
							 | 
						||
| 
								 | 
							
								    // variable (an integer) and if that happens to be in the middle
							 | 
						||
| 
								 | 
							
								    // of another thread reading it they will just get either the new
							 | 
						||
| 
								 | 
							
								    // or the old value.  Locking would achieve no more than this.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // It might be nice to check that we are being called from m_pGraph, but
							 | 
						||
| 
								 | 
							
								    // it turns out to be a millisecond or so per throw!
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // This is heuristics, these numbers are aimed at being "what works"
							 | 
						||
| 
								 | 
							
								    // rather than anything based on some theory.
							 | 
						||
| 
								 | 
							
								    // We use a hyperbola because it's easy to calculate and it includes
							 | 
						||
| 
								 | 
							
								    // a panic button asymptote (which we push off just to the left)
							 | 
						||
| 
								 | 
							
								    // The throttling fits the following table (roughly)
							 | 
						||
| 
								 | 
							
								    // Proportion   Throttle (msec)
							 | 
						||
| 
								 | 
							
								    //     >=1000         0
							 | 
						||
| 
								 | 
							
								    //        900         3
							 | 
						||
| 
								 | 
							
								    //        800         7
							 | 
						||
| 
								 | 
							
								    //        700        11
							 | 
						||
| 
								 | 
							
								    //        600        17
							 | 
						||
| 
								 | 
							
								    //        500        25
							 | 
						||
| 
								 | 
							
								    //        400        35
							 | 
						||
| 
								 | 
							
								    //        300        50
							 | 
						||
| 
								 | 
							
								    //        200        72
							 | 
						||
| 
								 | 
							
								    //        125       100
							 | 
						||
| 
								 | 
							
								    //        100       112
							 | 
						||
| 
								 | 
							
								    //         50       146
							 | 
						||
| 
								 | 
							
								    //          0       200
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // (some evidence that we could go for a sharper kink - e.g. no throttling
							 | 
						||
| 
								 | 
							
								    // until below the 750 mark - might give fractionally more frames on a
							 | 
						||
| 
								 | 
							
								    // P60-ish machine).  The easy way to get these coefficients is to use
							 | 
						||
| 
								 | 
							
								    // Renbase.xls follow the instructions therein using excel solver.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (q.Proportion>=1000) { m_trThrottle = 0; }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        // The DWORD is to make quite sure I get unsigned arithmetic
							 | 
						||
| 
								 | 
							
								        // as the constant is between 2**31 and 2**32
							 | 
						||
| 
								 | 
							
								        m_trThrottle = -330000 + (388880000/(q.Proportion+167));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								} // Notify
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Send a message to indicate what our supplier should do about quality.
							 | 
						||
| 
								 | 
							
								// Theory:
							 | 
						||
| 
								 | 
							
								// What a supplier wants to know is "is the frame I'm working on NOW
							 | 
						||
| 
								 | 
							
								// going to be late?".
							 | 
						||
| 
								 | 
							
								// F1 is the frame at the supplier (as above)
							 | 
						||
| 
								 | 
							
								// Tf1 is the due time for F1
							 | 
						||
| 
								 | 
							
								// T1 is the time at that point (NOW!)
							 | 
						||
| 
								 | 
							
								// Tr1 is the time that f1 WILL actually be rendered
							 | 
						||
| 
								 | 
							
								// L1 is the latency of the graph for frame F1 = Tr1-T1
							 | 
						||
| 
								 | 
							
								// D1 (for delay) is how late F1 will be beyond its due time i.e.
							 | 
						||
| 
								 | 
							
								// D1 = (Tr1-Tf1) which is what the supplier really wants to know.
							 | 
						||
| 
								 | 
							
								// Unfortunately Tr1 is in the future and is unknown, so is L1
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// We could estimate L1 by its value for a previous frame,
							 | 
						||
| 
								 | 
							
								// L0 = Tr0-T0 and work off
							 | 
						||
| 
								 | 
							
								// D1' = ((T1+L0)-Tf1) = (T1 + (Tr0-T0) -Tf1)
							 | 
						||
| 
								 | 
							
								// Rearranging terms:
							 | 
						||
| 
								 | 
							
								// D1' = (T1-T0) + (Tr0-Tf1)
							 | 
						||
| 
								 | 
							
								//       adding (Tf0-Tf0) and rearranging again:
							 | 
						||
| 
								 | 
							
								//     = (T1-T0) + (Tr0-Tf0) + (Tf0-Tf1)
							 | 
						||
| 
								 | 
							
								//     = (T1-T0) - (Tf1-Tf0) + (Tr0-Tf0)
							 | 
						||
| 
								 | 
							
								// But (Tr0-Tf0) is just D0 - how late frame zero was, and this is the
							 | 
						||
| 
								 | 
							
								// Late field in the quality message that we send.
							 | 
						||
| 
								 | 
							
								// The other two terms just state what correction should be applied before
							 | 
						||
| 
								 | 
							
								// using the lateness of F0 to predict the lateness of F1.
							 | 
						||
| 
								 | 
							
								// (T1-T0) says how much time has actually passed (we have lost this much)
							 | 
						||
| 
								 | 
							
								// (Tf1-Tf0) says how much time should have passed if we were keeping pace
							 | 
						||
| 
								 | 
							
								// (we have gained this much).
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Suppliers should therefore work off:
							 | 
						||
| 
								 | 
							
								//    Quality.Late + (T1-T0)  - (Tf1-Tf0)
							 | 
						||
| 
								 | 
							
								// and see if this is "acceptably late" or even early (i.e. negative).
							 | 
						||
| 
								 | 
							
								// They get T1 and T0 by polling the clock, they get Tf1 and Tf0 from
							 | 
						||
| 
								 | 
							
								// the time stamps in the frames.  They get Quality.Late from us.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseVideoRenderer::SendQuality(REFERENCE_TIME trLate,
							 | 
						||
| 
								 | 
							
								                                        REFERENCE_TIME trRealStream)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    Quality q;
							 | 
						||
| 
								 | 
							
								    HRESULT hr;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If we are the main user of time, then report this as Flood/Dry.
							 | 
						||
| 
								 | 
							
								    // If our suppliers are, then report it as Famine/Glut.
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // We need to take action, but avoid hunting.  Hunting is caused by
							 | 
						||
| 
								 | 
							
								    // 1. Taking too much action too soon and overshooting
							 | 
						||
| 
								 | 
							
								    // 2. Taking too long to react (so averaging can CAUSE hunting).
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // The reason why we use trLate as well as Wait is to reduce hunting;
							 | 
						||
| 
								 | 
							
								    // if the wait time is coming down and about to go into the red, we do
							 | 
						||
| 
								 | 
							
								    // NOT want to rely on some average which is only telling is that it used
							 | 
						||
| 
								 | 
							
								    // to be OK once.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    q.TimeStamp = (REFERENCE_TIME)trRealStream;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_trFrameAvg<0) {
							 | 
						||
| 
								 | 
							
								        q.Type = Famine;      // guess
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // Is the greater part of the time taken bltting or something else
							 | 
						||
| 
								 | 
							
								    else if (m_trFrameAvg > 2*m_trRenderAvg) {
							 | 
						||
| 
								 | 
							
								        q.Type = Famine;                        // mainly other
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        q.Type = Flood;                         // mainly bltting
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    q.Proportion = 1000;               // default
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_trFrameAvg<0) {
							 | 
						||
| 
								 | 
							
								        // leave it alone - we don't know enough
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if ( trLate> 0 ) {
							 | 
						||
| 
								 | 
							
								        // try to catch up over the next second
							 | 
						||
| 
								 | 
							
								        // We could be Really, REALLY late, but rendering all the frames
							 | 
						||
| 
								 | 
							
								        // anyway, just because it's so cheap.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        q.Proportion = 1000 - (int)((trLate)/(UNITS/1000));
							 | 
						||
| 
								 | 
							
								        if (q.Proportion<500) {
							 | 
						||
| 
								 | 
							
								           q.Proportion = 500;      // don't go daft. (could've been negative!)
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    } else if (  m_trWaitAvg>20000
							 | 
						||
| 
								 | 
							
								              && trLate<-20000
							 | 
						||
| 
								 | 
							
								              ){
							 | 
						||
| 
								 | 
							
								        // Go cautiously faster - aim at 2mSec wait.
							 | 
						||
| 
								 | 
							
								        if (m_trWaitAvg>=m_trFrameAvg) {
							 | 
						||
| 
								 | 
							
								            // This can happen because of some fudges.
							 | 
						||
| 
								 | 
							
								            // The waitAvg is how long we originally planned to wait
							 | 
						||
| 
								 | 
							
								            // The frameAvg is more honest.
							 | 
						||
| 
								 | 
							
								            // It means that we are spending a LOT of time waiting
							 | 
						||
| 
								 | 
							
								            q.Proportion = 2000;    // double.
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            if (m_trFrameAvg+20000 > m_trWaitAvg) {
							 | 
						||
| 
								 | 
							
								                q.Proportion
							 | 
						||
| 
								 | 
							
								                    = 1000 * (m_trFrameAvg / (m_trFrameAvg + 20000 - m_trWaitAvg));
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                // We're apparently spending more than the whole frame time waiting.
							 | 
						||
| 
								 | 
							
								                // Assume that the averages are slightly out of kilter, but that we
							 | 
						||
| 
								 | 
							
								                // are indeed doing a lot of waiting.  (This leg probably never
							 | 
						||
| 
								 | 
							
								                // happens, but the code avoids any potential divide by zero).
							 | 
						||
| 
								 | 
							
								                q.Proportion = 2000;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (q.Proportion>2000) {
							 | 
						||
| 
								 | 
							
								            q.Proportion = 2000;    // don't go crazy.
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Tell the supplier how late frames are when they get rendered
							 | 
						||
| 
								 | 
							
								    // That's how late we are now.
							 | 
						||
| 
								 | 
							
								    // If we are in directdraw mode then the guy upstream can see the drawing
							 | 
						||
| 
								 | 
							
								    // times and we'll just report on the start time.  He can figure out any
							 | 
						||
| 
								 | 
							
								    // offset to apply.  If we are in DIB Section mode then we will apply an
							 | 
						||
| 
								 | 
							
								    // extra offset which is half of our drawing time.  This is usually small
							 | 
						||
| 
								 | 
							
								    // but can sometimes be the dominant effect.  For this we will use the
							 | 
						||
| 
								 | 
							
								    // average drawing time rather than the last frame.  If the last frame took
							 | 
						||
| 
								 | 
							
								    // a long time to draw and made us late, that's already in the lateness
							 | 
						||
| 
								 | 
							
								    // figure.  We should not add it in again unless we expect the next frame
							 | 
						||
| 
								 | 
							
								    // to be the same.  We don't, we expect the average to be a better shot.
							 | 
						||
| 
								 | 
							
								    // In direct draw mode the RenderAvg will be zero.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    q.Late = trLate + m_trRenderAvg/2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // log what we're doing
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idQualityRate, q.Proportion);
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER( m_idQualityTime, (int)q.Late / 10000 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // A specific sink interface may be set through IPin
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_pQSink==NULL) {
							 | 
						||
| 
								 | 
							
								        // Get our input pin's peer.  We send quality management messages
							 | 
						||
| 
								 | 
							
								        // to any nominated receiver of these things (set in the IPin
							 | 
						||
| 
								 | 
							
								        // interface), or else to our source filter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        IQualityControl *pQC = NULL;
							 | 
						||
| 
								 | 
							
								        IPin *pOutputPin = m_pInputPin->GetConnected();
							 | 
						||
| 
								 | 
							
								        ASSERT(pOutputPin != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // And get an AddRef'd quality control interface
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        hr = pOutputPin->QueryInterface(IID_IQualityControl,(void**) &pQC);
							 | 
						||
| 
								 | 
							
								        if (SUCCEEDED(hr)) {
							 | 
						||
| 
								 | 
							
								            m_pQSink = pQC;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (m_pQSink) {
							 | 
						||
| 
								 | 
							
								        return m_pQSink->Notify(this,q);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return S_FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								} // SendQuality
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// We are called with a valid IMediaSample image to decide whether this is to
							 | 
						||
| 
								 | 
							
								// be drawn or not.  There must be a reference clock in operation.
							 | 
						||
| 
								 | 
							
								// Return S_OK if it is to be drawn Now (as soon as possible)
							 | 
						||
| 
								 | 
							
								// Return S_FALSE if it is to be drawn when it's due
							 | 
						||
| 
								 | 
							
								// Return an error if we want to drop it
							 | 
						||
| 
								 | 
							
								// m_nNormal=-1 indicates that we dropped the previous frame and so this
							 | 
						||
| 
								 | 
							
								// one should be drawn early.  Respect it and update it.
							 | 
						||
| 
								 | 
							
								// Use current stream time plus a number of heuristics (detailed below)
							 | 
						||
| 
								 | 
							
								// to make the decision
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HRESULT CBaseVideoRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,
							 | 
						||
| 
								 | 
							
								                                                __inout REFERENCE_TIME *ptrStart,
							 | 
						||
| 
								 | 
							
								                                                __inout REFERENCE_TIME *ptrEnd)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Don't call us unless there's a clock interface to synchronise with
							 | 
						||
| 
								 | 
							
								    ASSERT(m_pClock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idTimeStamp, (int)((*ptrStart)>>32));   // high order 32 bits
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idTimeStamp, (int)(*ptrStart));         // low order 32 bits
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // We lose a bit of time depending on the monitor type waiting for the next
							 | 
						||
| 
								 | 
							
								    // screen refresh.  On average this might be about 8mSec - so it will be
							 | 
						||
| 
								 | 
							
								    // later than we think when the picture appears.  To compensate a bit
							 | 
						||
| 
								 | 
							
								    // we bias the media samples by -8mSec i.e. 80000 UNITs.
							 | 
						||
| 
								 | 
							
								    // We don't ever make a stream time negative (call it paranoia)
							 | 
						||
| 
								 | 
							
								    if (*ptrStart>=80000) {
							 | 
						||
| 
								 | 
							
								        *ptrStart -= 80000;
							 | 
						||
| 
								 | 
							
								        *ptrEnd -= 80000;       // bias stop to to retain valid frame duration
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Cache the time stamp now.  We will want to compare what we did with what
							 | 
						||
| 
								 | 
							
								    // we started with (after making the monitor allowance).
							 | 
						||
| 
								 | 
							
								    m_trRememberStampForPerf = *ptrStart;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Get reference times (current and late)
							 | 
						||
| 
								 | 
							
								    REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.
							 | 
						||
| 
								 | 
							
								    m_pClock->GetTime(&trRealStream);
							 | 
						||
| 
								 | 
							
								#ifdef PERF
							 | 
						||
| 
								 | 
							
								    // While the reference clock is expensive:
							 | 
						||
| 
								 | 
							
								    // Remember the offset from timeGetTime and use that.
							 | 
						||
| 
								 | 
							
								    // This overflows all over the place, but when we subtract to get
							 | 
						||
| 
								 | 
							
								    // differences the overflows all cancel out.
							 | 
						||
| 
								 | 
							
								    m_llTimeOffset = trRealStream-timeGetTime()*10000;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    trRealStream -= m_tStart;     // convert to stream time (this is a reftime)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // We have to wory about two versions of "lateness".  The truth, which we
							 | 
						||
| 
								 | 
							
								    // try to work out here and the one measured against m_trTarget which
							 | 
						||
| 
								 | 
							
								    // includes long term feedback.  We report statistics against the truth
							 | 
						||
| 
								 | 
							
								    // but for operational decisions we work to the target.
							 | 
						||
| 
								 | 
							
								    // We use TimeDiff to make sure we get an integer because we
							 | 
						||
| 
								 | 
							
								    // may actually be late (or more likely early if there is a big time
							 | 
						||
| 
								 | 
							
								    // gap) by a very long time.
							 | 
						||
| 
								 | 
							
								    const int trTrueLate = TimeDiff(trRealStream - *ptrStart);
							 | 
						||
| 
								 | 
							
								    const int trLate = trTrueLate;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idSchLateTime, trTrueLate/10000);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Send quality control messages upstream, measured against target
							 | 
						||
| 
								 | 
							
								    HRESULT hr = SendQuality(trLate, trRealStream);
							 | 
						||
| 
								 | 
							
								    // Note: the filter upstream is allowed to this FAIL meaning "you do it".
							 | 
						||
| 
								 | 
							
								    m_bSupplierHandlingQuality = (hr==S_OK);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Decision time!  Do we drop, draw when ready or draw immediately?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const int trDuration = (int)(*ptrEnd - *ptrStart);
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // We need to see if the frame rate of the file has just changed.
							 | 
						||
| 
								 | 
							
								        // This would make comparing our previous frame rate with the current
							 | 
						||
| 
								 | 
							
								        // frame rate inefficent.  Hang on a moment though.  I've seen files
							 | 
						||
| 
								 | 
							
								        // where the frames vary between 33 and 34 mSec so as to average
							 | 
						||
| 
								 | 
							
								        // 30fps.  A minor variation like that won't hurt us.
							 | 
						||
| 
								 | 
							
								        int t = m_trDuration/32;
							 | 
						||
| 
								 | 
							
								        if (  trDuration > m_trDuration+t
							 | 
						||
| 
								 | 
							
								           || trDuration < m_trDuration-t
							 | 
						||
| 
								 | 
							
								           ) {
							 | 
						||
| 
								 | 
							
								            // There's a major variation.  Reset the average frame rate to
							 | 
						||
| 
								 | 
							
								            // exactly the current rate to disable decision 9002 for this frame,
							 | 
						||
| 
								 | 
							
								            // and remember the new rate.
							 | 
						||
| 
								 | 
							
								            m_trFrameAvg = trDuration;
							 | 
						||
| 
								 | 
							
								            m_trDuration = trDuration;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idEarliness, m_trEarliness/10000);
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idFrameAvg, m_trFrameAvg/10000);
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idWaitAvg, m_trWaitAvg/10000);
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idDuration, trDuration/10000);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef PERF
							 | 
						||
| 
								 | 
							
								    if (S_OK==pMediaSample->IsDiscontinuity()) {
							 | 
						||
| 
								 | 
							
								        MSR_INTEGER(m_idDecision, 9000);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Control the graceful slide back from slow to fast machine mode.
							 | 
						||
| 
								 | 
							
								    // After a frame drop accept an early frame and set the earliness to here
							 | 
						||
| 
								 | 
							
								    // If this frame is already later than the earliness then slide it to here
							 | 
						||
| 
								 | 
							
								    // otherwise do the standard slide (reduce by about 12% per frame).
							 | 
						||
| 
								 | 
							
								    // Note: earliness is normally NEGATIVE
							 | 
						||
| 
								 | 
							
								    BOOL bJustDroppedFrame
							 | 
						||
| 
								 | 
							
								        = (  m_bSupplierHandlingQuality
							 | 
						||
| 
								 | 
							
								          //  Can't use the pin sample properties because we might
							 | 
						||
| 
								 | 
							
								          //  not be in Receive when we call this
							 | 
						||
| 
								 | 
							
								          && (S_OK == pMediaSample->IsDiscontinuity())          // he just dropped one
							 | 
						||
| 
								 | 
							
								          )
							 | 
						||
| 
								 | 
							
								       || (m_nNormal==-1);                          // we just dropped one
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Set m_trEarliness (slide back from slow to fast machine mode)
							 | 
						||
| 
								 | 
							
								    if (trLate>0) {
							 | 
						||
| 
								 | 
							
								        m_trEarliness = 0;   // we are no longer in fast machine mode at all!
							 | 
						||
| 
								 | 
							
								    } else if (  (trLate>=m_trEarliness) || bJustDroppedFrame) {
							 | 
						||
| 
								 | 
							
								        m_trEarliness = trLate;  // Things have slipped of their own accord
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        m_trEarliness = m_trEarliness - m_trEarliness/8;  // graceful slide
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // prepare the new wait average - but don't pollute the old one until
							 | 
						||
| 
								 | 
							
								    // we have finished with it.
							 | 
						||
| 
								 | 
							
								    int trWaitAvg;
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // We never mix in a negative wait.  This causes us to believe in fast machines
							 | 
						||
| 
								 | 
							
								        // slightly more.
							 | 
						||
| 
								 | 
							
								        int trL = trLate<0 ? -trLate : 0;
							 | 
						||
| 
								 | 
							
								        trWaitAvg = (trL + m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int trFrame;
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        REFERENCE_TIME tr = trRealStream - m_trLastDraw; // Cd be large - 4 min pause!
							 | 
						||
| 
								 | 
							
								        if (tr>10000000) {
							 | 
						||
| 
								 | 
							
								            tr = 10000000;   // 1 second - arbitrarily.
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        trFrame = int(tr);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // We will DRAW this frame IF...
							 | 
						||
| 
								 | 
							
								    if (
							 | 
						||
| 
								 | 
							
								          // ...the time we are spending drawing is a small fraction of the total
							 | 
						||
| 
								 | 
							
								          // observed inter-frame time so that dropping it won't help much.
							 | 
						||
| 
								 | 
							
								          (3*m_trRenderAvg <= m_trFrameAvg)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								         // ...or our supplier is NOT handling things and the next frame would
							 | 
						||
| 
								 | 
							
								         // be less timely than this one or our supplier CLAIMS to be handling
							 | 
						||
| 
								 | 
							
								         // things, and is now less than a full FOUR frames late.
							 | 
						||
| 
								 | 
							
								       || ( m_bSupplierHandlingQuality
							 | 
						||
| 
								 | 
							
								          ? (trLate <= trDuration*4)
							 | 
						||
| 
								 | 
							
								          : (trLate+trLate < trDuration)
							 | 
						||
| 
								 | 
							
								          )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          // ...or we are on average waiting for over eight milliseconds then
							 | 
						||
| 
								 | 
							
								          // this may be just a glitch.  Draw it and we'll hope to catch up.
							 | 
						||
| 
								 | 
							
								       || (m_trWaitAvg > 80000)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          // ...or we haven't drawn an image for over a second.  We will update
							 | 
						||
| 
								 | 
							
								          // the display, which stops the video looking hung.
							 | 
						||
| 
								 | 
							
								          // Do this regardless of how late this media sample is.
							 | 
						||
| 
								 | 
							
								       || ((trRealStream - m_trLastDraw) > UNITS)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ) {
							 | 
						||
| 
								 | 
							
								        HRESULT Result;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // We are going to play this frame.  We may want to play it early.
							 | 
						||
| 
								 | 
							
								        // We will play it early if we think we are in slow machine mode.
							 | 
						||
| 
								 | 
							
								        // If we think we are NOT in slow machine mode, we will still play
							 | 
						||
| 
								 | 
							
								        // it early by m_trEarliness as this controls the graceful slide back.
							 | 
						||
| 
								 | 
							
								        // and in addition we aim at being m_trTarget late rather than "on time".
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        BOOL bPlayASAP = FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // we will play it AT ONCE (slow machine mode) if...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // ...we are playing catch-up
							 | 
						||
| 
								 | 
							
								        if ( bJustDroppedFrame) {
							 | 
						||
| 
								 | 
							
								            bPlayASAP = TRUE;
							 | 
						||
| 
								 | 
							
								            MSR_INTEGER(m_idDecision, 9001);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // ...or if we are running below the true frame rate
							 | 
						||
| 
								 | 
							
								            // exact comparisons are glitchy, for these measurements,
							 | 
						||
| 
								 | 
							
								            // so add an extra 5% or so
							 | 
						||
| 
								 | 
							
								        else if (  (m_trFrameAvg > trDuration + trDuration/16)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                   // It's possible to get into a state where we are losing ground, but
							 | 
						||
| 
								 | 
							
								                   // are a very long way ahead.  To avoid this or recover from it
							 | 
						||
| 
								 | 
							
								                   // we refuse to play early by more than 10 frames.
							 | 
						||
| 
								 | 
							
								                && (trLate > - trDuration*10)
							 | 
						||
| 
								 | 
							
								                ){
							 | 
						||
| 
								 | 
							
								            bPlayASAP = TRUE;
							 | 
						||
| 
								 | 
							
								            MSR_INTEGER(m_idDecision, 9002);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								#if 0
							 | 
						||
| 
								 | 
							
								            // ...or if we have been late and are less than one frame early
							 | 
						||
| 
								 | 
							
								        else if (  (trLate + trDuration > 0)
							 | 
						||
| 
								 | 
							
								                && (m_trWaitAvg<=20000)
							 | 
						||
| 
								 | 
							
								                ) {
							 | 
						||
| 
								 | 
							
								            bPlayASAP = TRUE;
							 | 
						||
| 
								 | 
							
								            MSR_INTEGER(m_idDecision, 9003);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								        // We will NOT play it at once if we are grossly early.  On very slow frame
							 | 
						||
| 
								 | 
							
								        // rate movies - e.g. clock.avi - it is not a good idea to leap ahead just
							 | 
						||
| 
								 | 
							
								        // because we got starved (for instance by the net) and dropped one frame
							 | 
						||
| 
								 | 
							
								        // some time or other.  If we are more than 900mSec early, then wait.
							 | 
						||
| 
								 | 
							
								        if (trLate<-9000000) {
							 | 
						||
| 
								 | 
							
								            bPlayASAP = FALSE;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (bPlayASAP) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            m_nNormal = 0;
							 | 
						||
| 
								 | 
							
								            MSR_INTEGER(m_idDecision, 0);
							 | 
						||
| 
								 | 
							
								            // When we are here, we are in slow-machine mode.  trLate may well
							 | 
						||
| 
								 | 
							
								            // oscillate between negative and positive when the supplier is
							 | 
						||
| 
								 | 
							
								            // dropping frames to keep sync.  We should not let that mislead
							 | 
						||
| 
								 | 
							
								            // us into thinking that we have as much as zero spare time!
							 | 
						||
| 
								 | 
							
								            // We just update with a zero wait.
							 | 
						||
| 
								 | 
							
								            m_trWaitAvg = (m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // Assume that we draw it immediately.  Update inter-frame stats
							 | 
						||
| 
								 | 
							
								            m_trFrameAvg = (trFrame + m_trFrameAvg*(AVGPERIOD-1))/AVGPERIOD;
							 | 
						||
| 
								 | 
							
								#ifndef PERF
							 | 
						||
| 
								 | 
							
								            // If this is NOT a perf build, then report what we know so far
							 | 
						||
| 
								 | 
							
								            // without looking at the clock any more.  This assumes that we
							 | 
						||
| 
								 | 
							
								            // actually wait for exactly the time we hope to.  It also reports
							 | 
						||
| 
								 | 
							
								            // how close we get to the manipulated time stamps that we now have
							 | 
						||
| 
								 | 
							
								            // rather than the ones we originally started with.  It will
							 | 
						||
| 
								 | 
							
								            // therefore be a little optimistic.  However it's fast.
							 | 
						||
| 
								 | 
							
								            PreparePerformanceData(trTrueLate, trFrame);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								            m_trLastDraw = trRealStream;
							 | 
						||
| 
								 | 
							
								            if (m_trEarliness > trLate) {
							 | 
						||
| 
								 | 
							
								                m_trEarliness = trLate;  // if we are actually early, this is neg
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            Result = S_OK;                   // Draw it now
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            ++m_nNormal;
							 | 
						||
| 
								 | 
							
								            // Set the average frame rate to EXACTLY the ideal rate.
							 | 
						||
| 
								 | 
							
								            // If we are exiting slow-machine mode then we will have caught up
							 | 
						||
| 
								 | 
							
								            // and be running ahead, so as we slide back to exact timing we will
							 | 
						||
| 
								 | 
							
								            // have a longer than usual gap at this point.  If we record this
							 | 
						||
| 
								 | 
							
								            // real gap then we'll think that we're running slow and go back
							 | 
						||
| 
								 | 
							
								            // into slow-machine mode and vever get it straight.
							 | 
						||
| 
								 | 
							
								            m_trFrameAvg = trDuration;
							 | 
						||
| 
								 | 
							
								            MSR_INTEGER(m_idDecision, 1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // Play it early by m_trEarliness and by m_trTarget
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                int trE = m_trEarliness;
							 | 
						||
| 
								 | 
							
								                if (trE < -m_trFrameAvg) {
							 | 
						||
| 
								 | 
							
								                    trE = -m_trFrameAvg;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                *ptrStart += trE;           // N.B. earliness is negative
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            int Delay = -trTrueLate;
							 | 
						||
| 
								 | 
							
								            Result = Delay<=0 ? S_OK : S_FALSE;     // OK = draw now, FALSE = wait
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            m_trWaitAvg = trWaitAvg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // Predict when it will actually be drawn and update frame stats
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (Result==S_FALSE) {   // We are going to wait
							 | 
						||
| 
								 | 
							
								                trFrame = TimeDiff(*ptrStart-m_trLastDraw);
							 | 
						||
| 
								 | 
							
								                m_trLastDraw = *ptrStart;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                // trFrame is already = trRealStream-m_trLastDraw;
							 | 
						||
| 
								 | 
							
								                m_trLastDraw = trRealStream;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								#ifndef PERF
							 | 
						||
| 
								 | 
							
								            int iAccuracy;
							 | 
						||
| 
								 | 
							
								            if (Delay>0) {
							 | 
						||
| 
								 | 
							
								                // Report lateness based on when we intend to play it
							 | 
						||
| 
								 | 
							
								                iAccuracy = TimeDiff(*ptrStart-m_trRememberStampForPerf);
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                // Report lateness based on playing it *now*.
							 | 
						||
| 
								 | 
							
								                iAccuracy = trTrueLate;     // trRealStream-RememberStampForPerf;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            PreparePerformanceData(iAccuracy, trFrame);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return Result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // We are going to drop this frame!
							 | 
						||
| 
								 | 
							
								    // Of course in DirectDraw mode the guy upstream may draw it anyway.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // This will probably give a large negative wack to the wait avg.
							 | 
						||
| 
								 | 
							
								    m_trWaitAvg = trWaitAvg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef PERF
							 | 
						||
| 
								 | 
							
								    // Respect registry setting - debug only!
							 | 
						||
| 
								 | 
							
								    if (m_bDrawLateFrames) {
							 | 
						||
| 
								 | 
							
								       return S_OK;                        // draw it when it's ready
							 | 
						||
| 
								 | 
							
								    }                                      // even though it's late.
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // We are going to drop this frame so draw the next one early
							 | 
						||
| 
								 | 
							
								    // n.b. if the supplier is doing direct draw then he may draw it anyway
							 | 
						||
| 
								 | 
							
								    // but he's doing something funny to arrive here in that case.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    MSR_INTEGER(m_idDecision, 2);
							 | 
						||
| 
								 | 
							
								    m_nNormal = -1;
							 | 
						||
| 
								 | 
							
								    return E_FAIL;                         // drop it
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								} // ShouldDrawSampleNow
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NOTE we're called by both the window thread and the source filter thread
							 | 
						||
| 
								 | 
							
								// so we have to be protected by a critical section (locked before called)
							 | 
						||
| 
								 | 
							
								// Also, when the window thread gets signalled to render an image, it always
							 | 
						||
| 
								 | 
							
								// does so regardless of how late it is. All the degradation is done when we
							 | 
						||
| 
								 | 
							
								// are scheduling the next sample to be drawn. Hence when we start an advise
							 | 
						||
| 
								 | 
							
								// link to draw a sample, that sample's time will always become the last one
							 | 
						||
| 
								 | 
							
								// drawn - unless of course we stop streaming in which case we cancel links
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								BOOL CBaseVideoRenderer::ScheduleSample(IMediaSample *pMediaSample)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // We override ShouldDrawSampleNow to add quality management
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    BOOL bDrawImage = CBaseRenderer::ScheduleSample(pMediaSample);
							 | 
						||
| 
								 | 
							
								    if (bDrawImage == FALSE) {
							 | 
						||
| 
								 | 
							
									++m_cFramesDropped;
							 | 
						||
| 
								 | 
							
									return FALSE;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // m_cFramesDrawn must NOT be updated here.  It has to be updated
							 | 
						||
| 
								 | 
							
								    // in RecordFrameLateness at the same time as the other statistics.
							 | 
						||
| 
								 | 
							
								    return TRUE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Implementation of IQualProp interface needed to support the property page
							 | 
						||
| 
								 | 
							
								// This is how the property page gets the data out of the scheduler. We are
							 | 
						||
| 
								 | 
							
								// passed into the constructor the owning object in the COM sense, this will
							 | 
						||
| 
								 | 
							
								// either be the video renderer or an external IUnknown if we're aggregated.
							 | 
						||
| 
								 | 
							
								// We initialise our CUnknown base class with this interface pointer. Then
							 | 
						||
| 
								 | 
							
								// all we have to do is to override NonDelegatingQueryInterface to expose
							 | 
						||
| 
								 | 
							
								// our IQualProp interface. The AddRef and Release are handled automatically
							 | 
						||
| 
								 | 
							
								// by the base class and will be passed on to the appropriate outer object
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::get_FramesDroppedInRenderer(__out int *pcFramesDropped)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(pcFramesDropped,E_POINTER);
							 | 
						||
| 
								 | 
							
								    CAutoLock cVideoLock(&m_InterfaceLock);
							 | 
						||
| 
								 | 
							
								    *pcFramesDropped = m_cFramesDropped;
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								} // get_FramesDroppedInRenderer
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Set *pcFramesDrawn to the number of frames drawn since
							 | 
						||
| 
								 | 
							
								// streaming started.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::get_FramesDrawn( int *pcFramesDrawn)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(pcFramesDrawn,E_POINTER);
							 | 
						||
| 
								 | 
							
								    CAutoLock cVideoLock(&m_InterfaceLock);
							 | 
						||
| 
								 | 
							
								    *pcFramesDrawn = m_cFramesDrawn;
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								} // get_FramesDrawn
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Set iAvgFrameRate to the frames per hundred secs since
							 | 
						||
| 
								 | 
							
								// streaming started.  0 otherwise.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::get_AvgFrameRate( int *piAvgFrameRate)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(piAvgFrameRate,E_POINTER);
							 | 
						||
| 
								 | 
							
								    CAutoLock cVideoLock(&m_InterfaceLock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int t;
							 | 
						||
| 
								 | 
							
								    if (m_bStreaming) {
							 | 
						||
| 
								 | 
							
								        t = timeGetTime()-m_tStreamingStart;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        t = m_tStreamingStart;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (t<=0) {
							 | 
						||
| 
								 | 
							
								        *piAvgFrameRate = 0;
							 | 
						||
| 
								 | 
							
								        ASSERT(m_cFramesDrawn == 0);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        // i is frames per hundred seconds
							 | 
						||
| 
								 | 
							
								        *piAvgFrameRate = MulDiv(100000, m_cFramesDrawn, t);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								} // get_AvgFrameRate
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Set *piAvg to the average sync offset since streaming started
							 | 
						||
| 
								 | 
							
								// in mSec.  The sync offset is the time in mSec between when the frame
							 | 
						||
| 
								 | 
							
								// should have been drawn and when the frame was actually drawn.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::get_AvgSyncOffset(__out int *piAvg)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(piAvg,E_POINTER);
							 | 
						||
| 
								 | 
							
								    CAutoLock cVideoLock(&m_InterfaceLock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (NULL==m_pClock) {
							 | 
						||
| 
								 | 
							
								        *piAvg = 0;
							 | 
						||
| 
								 | 
							
								        return NOERROR;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Note that we didn't gather the stats on the first frame
							 | 
						||
| 
								 | 
							
								    // so we use m_cFramesDrawn-1 here
							 | 
						||
| 
								 | 
							
								    if (m_cFramesDrawn<=1) {
							 | 
						||
| 
								 | 
							
								        *piAvg = 0;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        *piAvg = (int)(m_iTotAcc / (m_cFramesDrawn-1));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								} // get_AvgSyncOffset
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// To avoid dragging in the maths library - a cheap
							 | 
						||
| 
								 | 
							
								// approximate integer square root.
							 | 
						||
| 
								 | 
							
								// We do this by getting a starting guess which is between 1
							 | 
						||
| 
								 | 
							
								// and 2 times too large, followed by THREE iterations of
							 | 
						||
| 
								 | 
							
								// Newton Raphson.  (That will give accuracy to the nearest mSec
							 | 
						||
| 
								 | 
							
								// for the range in question - roughly 0..1000)
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// It would be faster to use a linear interpolation and ONE NR, but
							 | 
						||
| 
								 | 
							
								// who cares.  If anyone does - the best linear interpolation is
							 | 
						||
| 
								 | 
							
								// to approximates sqrt(x) by
							 | 
						||
| 
								 | 
							
								// y = x * (sqrt(2)-1) + 1 - 1/sqrt(2) + 1/(8*(sqrt(2)-1))
							 | 
						||
| 
								 | 
							
								// 0r y = x*0.41421 + 0.59467
							 | 
						||
| 
								 | 
							
								// This minimises the maximal error in the range in question.
							 | 
						||
| 
								 | 
							
								// (error is about +0.008883 and then one NR will give error .0000something
							 | 
						||
| 
								 | 
							
								// (Of course these are integers, so you can't just multiply by 0.41421
							 | 
						||
| 
								 | 
							
								// you'd have to do some sort of MulDiv).
							 | 
						||
| 
								 | 
							
								// Anyone wanna check my maths?  (This is only for a property display!)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int isqrt(int x)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    int s = 1;
							 | 
						||
| 
								 | 
							
								    // Make s an initial guess for sqrt(x)
							 | 
						||
| 
								 | 
							
								    if (x > 0x40000000) {
							 | 
						||
| 
								 | 
							
								       s = 0x8000;     // prevent any conceivable closed loop
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        while (s*s<x) {    // loop cannot possible go more than 31 times
							 | 
						||
| 
								 | 
							
								            s = 2*s;       // normally it goes about 6 times
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // Three NR iterations.
							 | 
						||
| 
								 | 
							
								        if (x==0) {
							 | 
						||
| 
								 | 
							
								           s= 0; // Wouldn't it be tragic to divide by zero whenever our
							 | 
						||
| 
								 | 
							
								                 // accuracy was perfect!
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            s = (s*s+x)/(2*s);
							 | 
						||
| 
								 | 
							
								            if (s>=0) s = (s*s+x)/(2*s);
							 | 
						||
| 
								 | 
							
								            if (s>=0) s = (s*s+x)/(2*s);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return s;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//  Do estimates for standard deviations for per-frame
							 | 
						||
| 
								 | 
							
								//  statistics
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								HRESULT CBaseVideoRenderer::GetStdDev(
							 | 
						||
| 
								 | 
							
								    int nSamples,
							 | 
						||
| 
								 | 
							
								    __out int *piResult,
							 | 
						||
| 
								 | 
							
								    LONGLONG llSumSq,
							 | 
						||
| 
								 | 
							
								    LONGLONG iTot
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    CheckPointer(piResult,E_POINTER);
							 | 
						||
| 
								 | 
							
								    CAutoLock cVideoLock(&m_InterfaceLock);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (NULL==m_pClock) {
							 | 
						||
| 
								 | 
							
								        *piResult = 0;
							 | 
						||
| 
								 | 
							
								        return NOERROR;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // If S is the Sum of the Squares of observations and
							 | 
						||
| 
								 | 
							
								    //    T the Total (i.e. sum) of the observations and there were
							 | 
						||
| 
								 | 
							
								    //    N observations, then an estimate of the standard deviation is
							 | 
						||
| 
								 | 
							
								    //      sqrt( (S - T**2/N) / (N-1) )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (nSamples<=1) {
							 | 
						||
| 
								 | 
							
								        *piResult = 0;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        LONGLONG x;
							 | 
						||
| 
								 | 
							
								        // First frames have invalid stamps, so we get no stats for them
							 | 
						||
| 
								 | 
							
								        // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // so we use m_cFramesDrawn-1 here
							 | 
						||
| 
								 | 
							
								        x = llSumSq - llMulDiv(iTot, iTot, nSamples, 0);
							 | 
						||
| 
								 | 
							
								        x = x / (nSamples-1);
							 | 
						||
| 
								 | 
							
								        ASSERT(x>=0);
							 | 
						||
| 
								 | 
							
								        *piResult = isqrt((LONG)x);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return NOERROR;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Set *piDev to the standard deviation in mSec of the sync offset
							 | 
						||
| 
								 | 
							
								// of each frame since streaming started.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::get_DevSyncOffset(__out int *piDev)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // First frames have invalid stamps, so we get no stats for them
							 | 
						||
| 
								 | 
							
								    // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
							 | 
						||
| 
								 | 
							
								    return GetStdDev(m_cFramesDrawn - 1,
							 | 
						||
| 
								 | 
							
								                     piDev,
							 | 
						||
| 
								 | 
							
								                     m_iSumSqAcc,
							 | 
						||
| 
								 | 
							
								                     m_iTotAcc);
							 | 
						||
| 
								 | 
							
								} // get_DevSyncOffset
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Set *piJitter to the standard deviation in mSec of the inter-frame time
							 | 
						||
| 
								 | 
							
								// of frames since streaming started.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP CBaseVideoRenderer::get_Jitter(__out int *piJitter)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // First frames have invalid stamps, so we get no stats for them
							 | 
						||
| 
								 | 
							
								    // So second frame gives invalid inter-frame time
							 | 
						||
| 
								 | 
							
								    // So we need 3 frames to get 1 datum, so N is cFramesDrawn-2
							 | 
						||
| 
								 | 
							
								    return GetStdDev(m_cFramesDrawn - 2,
							 | 
						||
| 
								 | 
							
								                     piJitter,
							 | 
						||
| 
								 | 
							
								                     m_iSumSqFrameTime,
							 | 
						||
| 
								 | 
							
								                     m_iSumFrameTime);
							 | 
						||
| 
								 | 
							
								} // get_Jitter
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Overidden to return our IQualProp interface
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP
							 | 
						||
| 
								 | 
							
								CBaseVideoRenderer::NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // We return IQualProp and delegate everything else
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (riid == IID_IQualProp) {
							 | 
						||
| 
								 | 
							
								        return GetInterface( (IQualProp *)this, ppv);
							 | 
						||
| 
								 | 
							
								    } else if (riid == IID_IQualityControl) {
							 | 
						||
| 
								 | 
							
								        return GetInterface( (IQualityControl *)this, ppv);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return CBaseRenderer::NonDelegatingQueryInterface(riid,ppv);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Override JoinFilterGraph so that, just before leaving
							 | 
						||
| 
								 | 
							
								// the graph we can send an EC_WINDOW_DESTROYED event
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								STDMETHODIMP
							 | 
						||
| 
								 | 
							
								CBaseVideoRenderer::JoinFilterGraph(__inout_opt IFilterGraph *pGraph, __in_opt LPCWSTR pName)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // Since we send EC_ACTIVATE, we also need to ensure
							 | 
						||
| 
								 | 
							
								    // we send EC_WINDOW_DESTROYED or the resource manager may be
							 | 
						||
| 
								 | 
							
								    // holding us as a focus object
							 | 
						||
| 
								 | 
							
								    if (!pGraph && m_pGraph) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // We were in a graph and now we're not
							 | 
						||
| 
								 | 
							
								        // Do this properly in case we are aggregated
							 | 
						||
| 
								 | 
							
								        IBaseFilter* pFilter = this;
							 | 
						||
| 
								 | 
							
								        NotifyEvent(EC_WINDOW_DESTROYED, (LPARAM) pFilter, 0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return CBaseFilter::JoinFilterGraph(pGraph, pName);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// This removes a large number of level 4 warnings from the
							 | 
						||
| 
								 | 
							
								// Microsoft compiler which in this case are not very useful
							 | 
						||
| 
								 | 
							
								#pragma warning(disable: 4514)
							 | 
						||
| 
								 | 
							
								
							 |