renbase.cpp
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:97k
源码类别:

P2P编程

开发平台:

Visual C++

  1. //------------------------------------------------------------------------------
  2. // File: RenBase.cpp
  3. //
  4. // Desc: DirectShow base classes.
  5. //
  6. // Copyright (c) Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8. #include <streams.h>        // DirectShow base class definitions
  9. #include <mmsystem.h>       // Needed for definition of timeGetTime
  10. #include <limits.h>         // Standard data type limit definitions
  11. #include <measure.h>        // Used for time critical log functions
  12. #pragma warning(disable:4355)
  13. //  Helper function for clamping time differences
  14. int inline TimeDiff(REFERENCE_TIME rt)
  15. {
  16.     if (rt < - (50 * UNITS)) {
  17.         return -(50 * UNITS);
  18.     } else
  19.     if (rt > 50 * UNITS) {
  20.         return 50 * UNITS;
  21.     } else return (int)rt;
  22. }
  23. // Implements the CBaseRenderer class
  24. CBaseRenderer::CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer
  25.                              TCHAR *pName,         // Debug ONLY description
  26.                              LPUNKNOWN pUnk,       // Aggregated owner object
  27.                              HRESULT *phr) :       // General OLE return code
  28.     CBaseFilter(pName,pUnk,&m_InterfaceLock,RenderClass),
  29.     m_evComplete(TRUE),
  30.     m_bAbort(FALSE),
  31.     m_pPosition(NULL),
  32.     m_ThreadSignal(TRUE),
  33.     m_bStreaming(FALSE),
  34.     m_bEOS(FALSE),
  35.     m_bEOSDelivered(FALSE),
  36.     m_pMediaSample(NULL),
  37.     m_dwAdvise(0),
  38.     m_pQSink(NULL),
  39.     m_pInputPin(NULL),
  40.     m_bRepaintStatus(TRUE),
  41.     m_SignalTime(0),
  42.     m_bInReceive(FALSE),
  43.     m_EndOfStreamTimer(0)
  44. {
  45.     Ready();
  46. #ifdef PERF
  47.     m_idBaseStamp = MSR_REGISTER(TEXT("BaseRenderer: sample time stamp"));
  48.     m_idBaseRenderTime = MSR_REGISTER(TEXT("BaseRenderer: draw time (msec)"));
  49.     m_idBaseAccuracy = MSR_REGISTER(TEXT("BaseRenderer: Accuracy (msec)"));
  50. #endif
  51. }
  52. // Delete the dynamically allocated IMediaPosition and IMediaSeeking helper
  53. // object. The object is created when somebody queries us. These are standard
  54. // control interfaces for seeking and setting start/stop positions and rates.
  55. // We will probably also have made an input pin based on CRendererInputPin
  56. // that has to be deleted, it's created when an enumerator calls our GetPin
  57. CBaseRenderer::~CBaseRenderer()
  58. {
  59.     ASSERT(m_bStreaming == FALSE);
  60.     ASSERT(m_EndOfStreamTimer == 0);
  61.     StopStreaming();
  62.     ClearPendingSample();
  63.     // Delete any IMediaPosition implementation
  64.     if (m_pPosition) {
  65.         delete m_pPosition;
  66.         m_pPosition = NULL;
  67.     }
  68.     // Delete any input pin created
  69.     if (m_pInputPin) {
  70.         delete m_pInputPin;
  71.         m_pInputPin = NULL;
  72.     }
  73.     // Release any Quality sink
  74.     ASSERT(m_pQSink == NULL);
  75. }
  76. // This returns the IMediaPosition and IMediaSeeking interfaces
  77. HRESULT CBaseRenderer::GetMediaPositionInterface(REFIID riid,void **ppv)
  78. {
  79.     CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
  80.     if (m_pPosition) {
  81.         return m_pPosition->NonDelegatingQueryInterface(riid,ppv);
  82.     }
  83.     HRESULT hr = NOERROR;
  84.     // Create implementation of this dynamically since sometimes we may
  85.     // never try and do a seek. The helper object implements a position
  86.     // control interface (IMediaPosition) which in fact simply takes the
  87.     // calls normally from the filter graph and passes them upstream
  88.     m_pPosition = new CRendererPosPassThru(NAME("Renderer CPosPassThru"),
  89.                                            CBaseFilter::GetOwner(),
  90.                                            (HRESULT *) &hr,
  91.                                            GetPin(0));
  92.     if (m_pPosition == NULL) {
  93.         return E_OUTOFMEMORY;
  94.     }
  95.     if (FAILED(hr)) {
  96.         delete m_pPosition;
  97.         m_pPosition = NULL;
  98.         return E_NOINTERFACE;
  99.     }
  100.     return GetMediaPositionInterface(riid,ppv);
  101. }
  102. // Overriden to say what interfaces we support and where
  103. STDMETHODIMP CBaseRenderer::NonDelegatingQueryInterface(REFIID riid,void **ppv)
  104. {
  105.     // Do we have this interface
  106.     if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
  107.         return GetMediaPositionInterface(riid,ppv);
  108.     } else {
  109.         return CBaseFilter::NonDelegatingQueryInterface(riid,ppv);
  110.     }
  111. }
  112. // This is called whenever we change states, we have a manual reset event that
  113. // is signalled whenever we don't won't the source filter thread to wait in us
  114. // (such as in a stopped state) and likewise is not signalled whenever it can
  115. // wait (during paused and running) this function sets or resets the thread
  116. // event. The event is used to stop source filter threads waiting in Receive
  117. HRESULT CBaseRenderer::SourceThreadCanWait(BOOL bCanWait)
  118. {
  119.     if (bCanWait == TRUE) {
  120.         m_ThreadSignal.Reset();
  121.     } else {
  122.         m_ThreadSignal.Set();
  123.     }
  124.     return NOERROR;
  125. }
  126. #ifdef DEBUG
  127. // Dump the current renderer state to the debug terminal. The hardest part of
  128. // the renderer is the window where we unlock everything to wait for a clock
  129. // to signal it is time to draw or for the application to cancel everything
  130. // by stopping the filter. If we get things wrong we can leave the thread in
  131. // WaitForRenderTime with no way for it to ever get out and we will deadlock
  132. void CBaseRenderer::DisplayRendererState()
  133. {
  134.     DbgLog((LOG_TIMING, 1, TEXT("nTimed out in WaitForRenderTime")));
  135.     // No way should this be signalled at this point
  136.     BOOL bSignalled = m_ThreadSignal.Check();
  137.     DbgLog((LOG_TIMING, 1, TEXT("Signal sanity check %d"),bSignalled));
  138.     // Now output the current renderer state variables
  139.     DbgLog((LOG_TIMING, 1, TEXT("Filter state %d"),m_State));
  140.     DbgLog((LOG_TIMING, 1, TEXT("Abort flag %d"),m_bAbort));
  141.     DbgLog((LOG_TIMING, 1, TEXT("Streaming flag %d"),m_bStreaming));
  142.     DbgLog((LOG_TIMING, 1, TEXT("Clock advise link %d"),m_dwAdvise));
  143.     DbgLog((LOG_TIMING, 1, TEXT("Current media sample %x"),m_pMediaSample));
  144.     DbgLog((LOG_TIMING, 1, TEXT("EOS signalled %d"),m_bEOS));
  145.     DbgLog((LOG_TIMING, 1, TEXT("EOS delivered %d"),m_bEOSDelivered));
  146.     DbgLog((LOG_TIMING, 1, TEXT("Repaint status %d"),m_bRepaintStatus));
  147.     // Output the delayed end of stream timer information
  148.     DbgLog((LOG_TIMING, 1, TEXT("End of stream timer %x"),m_EndOfStreamTimer));
  149.     DbgLog((LOG_TIMING, 1, TEXT("Deliver time %s"),CDisp((LONGLONG)m_SignalTime)));
  150.     // Should never timeout during a flushing state
  151.     BOOL bFlushing = m_pInputPin->IsFlushing();
  152.     DbgLog((LOG_TIMING, 1, TEXT("Flushing sanity check %d"),bFlushing));
  153.     // Display the time we were told to start at
  154.     DbgLog((LOG_TIMING, 1, TEXT("Last run time %s"),CDisp((LONGLONG)m_tStart.m_time)));
  155.     // Have we got a reference clock
  156.     if (m_pClock == NULL) return;
  157.     // Get the current time from the wall clock
  158.     CRefTime CurrentTime,StartTime,EndTime;
  159.     m_pClock->GetTime((REFERENCE_TIME*) &CurrentTime);
  160.     CRefTime Offset = CurrentTime - m_tStart;
  161.     // Display the current time from the clock
  162.     DbgLog((LOG_TIMING, 1, TEXT("Clock time %s"),CDisp((LONGLONG)CurrentTime.m_time)));
  163.     DbgLog((LOG_TIMING, 1, TEXT("Time difference %dms"),Offset.Millisecs()));
  164.     // Do we have a sample ready to render
  165.     if (m_pMediaSample == NULL) return;
  166.     m_pMediaSample->GetTime((REFERENCE_TIME*)&StartTime, (REFERENCE_TIME*)&EndTime);
  167.     DbgLog((LOG_TIMING, 1, TEXT("Next sample stream times (Start %d End %d ms)"),
  168.            StartTime.Millisecs(),EndTime.Millisecs()));
  169.     // Calculate how long it is until it is due for rendering
  170.     CRefTime Wait = (m_tStart + StartTime) - CurrentTime;
  171.     DbgLog((LOG_TIMING, 1, TEXT("Wait required %d ms"),Wait.Millisecs()));
  172. }
  173. #endif
  174. // Wait until the clock sets the timer event or we're otherwise signalled. We
  175. // set an arbitrary timeout for this wait and if it fires then we display the
  176. // current renderer state on the debugger. It will often fire if the filter's
  177. // left paused in an application however it may also fire during stress tests
  178. // if the synchronisation with application seeks and state changes is faulty
  179. #define RENDER_TIMEOUT 10000
  180. HRESULT CBaseRenderer::WaitForRenderTime()
  181. {
  182.     HANDLE WaitObjects[] = { m_ThreadSignal, m_RenderEvent };
  183.     DWORD Result = WAIT_TIMEOUT;
  184.     // Wait for either the time to arrive or for us to be stopped
  185.     OnWaitStart();
  186.     while (Result == WAIT_TIMEOUT) {
  187.         Result = WaitForMultipleObjects(2,WaitObjects,FALSE,RENDER_TIMEOUT);
  188. #ifdef DEBUG
  189.         if (Result == WAIT_TIMEOUT) DisplayRendererState();
  190. #endif
  191.     }
  192.     OnWaitEnd();
  193.     // We may have been awoken without the timer firing
  194.     if (Result == WAIT_OBJECT_0) {
  195.         return VFW_E_STATE_CHANGED;
  196.     }
  197.     SignalTimerFired();
  198.     return NOERROR;
  199. }
  200. // Poll waiting for Receive to complete.  This really matters when
  201. // Receive may set the palette and cause window messages
  202. // The problem is that if we don't really wait for a renderer to
  203. // stop processing we can deadlock waiting for a transform which
  204. // is calling the renderer's Receive() method because the transform's
  205. // Stop method doesn't know to process window messages to unblock
  206. // the renderer's Receive processing
  207. void CBaseRenderer::WaitForReceiveToComplete()
  208. {
  209.     for (;;) {
  210.         if (!m_bInReceive) {
  211.             break;
  212.         }
  213.         MSG msg;
  214.         //  Receive all interthread sendmessages
  215.         PeekMessage(&msg, NULL, WM_NULL, WM_NULL, PM_NOREMOVE);
  216.         Sleep(1);
  217.     }
  218.     // If the wakebit for QS_POSTMESSAGE is set, the PeekMessage call
  219.     // above just cleared the changebit which will cause some messaging
  220.     // calls to block (waitMessage, MsgWaitFor...) now.
  221.     // Post a dummy message to set the QS_POSTMESSAGE bit again
  222.     if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
  223.         //  Send dummy message
  224.         PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
  225.     }
  226. }
  227. // A filter can have four discrete states, namely Stopped, Running, Paused,
  228. // Intermediate. We are in an intermediate state if we are currently trying
  229. // to pause but haven't yet got the first sample (or if we have been flushed
  230. // in paused state and therefore still have to wait for a sample to arrive)
  231. // This class contains an event called m_evComplete which is signalled when
  232. // the current state is completed and is not signalled when we are waiting to
  233. // complete the last state transition. As mentioned above the only time we
  234. // use this at the moment is when we wait for a media sample in paused state
  235. // If while we are waiting we receive an end of stream notification from the
  236. // source filter then we know no data is imminent so we can reset the event
  237. // This means that when we transition to paused the source filter must call
  238. // end of stream on us or send us an image otherwise we'll hang indefinately
  239. // Simple internal way of getting the real state
  240. FILTER_STATE CBaseRenderer::GetRealState() {
  241.     return m_State;
  242. }
  243. // The renderer doesn't complete the full transition to paused states until
  244. // it has got one media sample to render. If you ask it for its state while
  245. // it's waiting it will return the state along with VFW_S_STATE_INTERMEDIATE
  246. STDMETHODIMP CBaseRenderer::GetState(DWORD dwMSecs,FILTER_STATE *State)
  247. {
  248.     CheckPointer(State,E_POINTER);
  249.     if (WaitDispatchingMessages(m_evComplete, dwMSecs) == WAIT_TIMEOUT) {
  250.         *State = m_State;
  251.         return VFW_S_STATE_INTERMEDIATE;
  252.     }
  253.     *State = m_State;
  254.     return NOERROR;
  255. }
  256. // If we're pausing and we have no samples we don't complete the transition
  257. // to State_Paused and we return S_FALSE. However if the m_bAbort flag has
  258. // been set then all samples are rejected so there is no point waiting for
  259. // one. If we do have a sample then return NOERROR. We will only ever return
  260. // VFW_S_STATE_INTERMEDIATE from GetState after being paused with no sample
  261. // (calling GetState after either being stopped or Run will NOT return this)
  262. HRESULT CBaseRenderer::CompleteStateChange(FILTER_STATE OldState)
  263. {
  264.     // Allow us to be paused when disconnected
  265.     if (m_pInputPin->IsConnected() == FALSE) {
  266.         Ready();
  267.         return S_OK;
  268.     }
  269.     // Have we run off the end of stream
  270.     if (IsEndOfStream() == TRUE) {
  271.         Ready();
  272.         return S_OK;
  273.     }
  274.     // Make sure we get fresh data after being stopped
  275.     if (HaveCurrentSample() == TRUE) {
  276.         if (OldState != State_Stopped) {
  277.             Ready();
  278.             return S_OK;
  279.         }
  280.     }
  281.     NotReady();
  282.     return S_FALSE;
  283. }
  284. // When we stop the filter the things we do are:-
  285. //      Decommit the allocator being used in the connection
  286. //      Release the source filter if it's waiting in Receive
  287. //      Cancel any advise link we set up with the clock
  288. //      Any end of stream signalled is now obsolete so reset
  289. //      Allow us to be stopped when we are not connected
  290. STDMETHODIMP CBaseRenderer::Stop()
  291. {
  292.     CAutoLock cRendererLock(&m_InterfaceLock);
  293.     // Make sure there really is a state change
  294.     if (m_State == State_Stopped) {
  295.         return NOERROR;
  296.     }
  297.     // Is our input pin connected
  298.     if (m_pInputPin->IsConnected() == FALSE) {
  299.         NOTE("Input pin is not connected");
  300.         m_State = State_Stopped;
  301.         return NOERROR;
  302.     }
  303.     CBaseFilter::Stop();
  304.     // If we are going into a stopped state then we must decommit whatever
  305.     // allocator we are using it so that any source filter waiting in the
  306.     // GetBuffer can be released and unlock themselves for a state change
  307.     if (m_pInputPin->Allocator()) {
  308.         m_pInputPin->Allocator()->Decommit();
  309.     }
  310.     // Cancel any scheduled rendering
  311.     SetRepaintStatus(TRUE);
  312.     StopStreaming();
  313.     SourceThreadCanWait(FALSE);
  314.     ResetEndOfStream();
  315.     CancelNotification();
  316.     // There should be no outstanding clock advise
  317.     ASSERT(CancelNotification() == S_FALSE);
  318.     ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
  319.     ASSERT(m_EndOfStreamTimer == 0);
  320.     Ready();
  321.     WaitForReceiveToComplete();
  322.     m_bAbort = FALSE;
  323.     return NOERROR;
  324. }
  325. // When we pause the filter the things we do are:-
  326. //      Commit the allocator being used in the connection
  327. //      Allow a source filter thread to wait in Receive
  328. //      Cancel any clock advise link (we may be running)
  329. //      Possibly complete the state change if we have data
  330. //      Allow us to be paused when we are not connected
  331. STDMETHODIMP CBaseRenderer::Pause()
  332. {
  333.     CAutoLock cRendererLock(&m_InterfaceLock);
  334.     FILTER_STATE OldState = m_State;
  335.     ASSERT(m_pInputPin->IsFlushing() == FALSE);
  336.     // Make sure there really is a state change
  337.     if (m_State == State_Paused) {
  338.         return CompleteStateChange(State_Paused);
  339.     }
  340.     // Has our input pin been connected
  341.     if (m_pInputPin->IsConnected() == FALSE) {
  342.         NOTE("Input pin is not connected");
  343.         m_State = State_Paused;
  344.         return CompleteStateChange(State_Paused);
  345.     }
  346.     // Pause the base filter class
  347.     HRESULT hr = CBaseFilter::Pause();
  348.     if (FAILED(hr)) {
  349.         NOTE("Pause failed");
  350.         return hr;
  351.     }
  352.     // Enable EC_REPAINT events again
  353.     SetRepaintStatus(TRUE);
  354.     StopStreaming();
  355.     SourceThreadCanWait(TRUE);
  356.     CancelNotification();
  357.     ResetEndOfStreamTimer();
  358.     // If we are going into a paused state then we must commit whatever
  359.     // allocator we are using it so that any source filter can call the
  360.     // GetBuffer and expect to get a buffer without returning an error
  361.     if (m_pInputPin->Allocator()) {
  362.         m_pInputPin->Allocator()->Commit();
  363.     }
  364.     // There should be no outstanding advise
  365.     ASSERT(CancelNotification() == S_FALSE);
  366.     ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
  367.     ASSERT(m_EndOfStreamTimer == 0);
  368.     ASSERT(m_pInputPin->IsFlushing() == FALSE);
  369.     // When we come out of a stopped state we must clear any image we were
  370.     // holding onto for frame refreshing. Since renderers see state changes
  371.     // first we can reset ourselves ready to accept the source thread data
  372.     // Paused or running after being stopped causes the current position to
  373.     // be reset so we're not interested in passing end of stream signals
  374.     if (OldState == State_Stopped) {
  375.         m_bAbort = FALSE;
  376.         ClearPendingSample();
  377.     }
  378.     return CompleteStateChange(OldState);
  379. }
  380. // When we run the filter the things we do are:-
  381. //      Commit the allocator being used in the connection
  382. //      Allow a source filter thread to wait in Receive
  383. //      Signal the render event just to get us going
  384. //      Start the base class by calling StartStreaming
  385. //      Allow us to be run when we are not connected
  386. //      Signal EC_COMPLETE if we are not connected
  387. STDMETHODIMP CBaseRenderer::Run(REFERENCE_TIME StartTime)
  388. {
  389.     CAutoLock cRendererLock(&m_InterfaceLock);
  390.     FILTER_STATE OldState = m_State;
  391.     // Make sure there really is a state change
  392.     if (m_State == State_Running) {
  393.         return NOERROR;
  394.     }
  395.     // Send EC_COMPLETE if we're not connected
  396.     if (m_pInputPin->IsConnected() == FALSE) {
  397.         NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
  398.         m_State = State_Running;
  399.         return NOERROR;
  400.     }
  401.     Ready();
  402.     // Pause the base filter class
  403.     HRESULT hr = CBaseFilter::Run(StartTime);
  404.     if (FAILED(hr)) {
  405.         NOTE("Run failed");
  406.         return hr;
  407.     }
  408.     // Allow the source thread to wait
  409.     ASSERT(m_pInputPin->IsFlushing() == FALSE);
  410.     SourceThreadCanWait(TRUE);
  411.     SetRepaintStatus(FALSE);
  412.     // There should be no outstanding advise
  413.     ASSERT(CancelNotification() == S_FALSE);
  414.     ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
  415.     ASSERT(m_EndOfStreamTimer == 0);
  416.     ASSERT(m_pInputPin->IsFlushing() == FALSE);
  417.     // If we are going into a running state then we must commit whatever
  418.     // allocator we are using it so that any source filter can call the
  419.     // GetBuffer and expect to get a buffer without returning an error
  420.     if (m_pInputPin->Allocator()) {
  421.         m_pInputPin->Allocator()->Commit();
  422.     }
  423.     // When we come out of a stopped state we must clear any image we were
  424.     // holding onto for frame refreshing. Since renderers see state changes
  425.     // first we can reset ourselves ready to accept the source thread data
  426.     // Paused or running after being stopped causes the current position to
  427.     // be reset so we're not interested in passing end of stream signals
  428.     if (OldState == State_Stopped) {
  429.         m_bAbort = FALSE;
  430.         ClearPendingSample();
  431.     }
  432.     return StartStreaming();
  433. }
  434. // Return the number of input pins we support
  435. int CBaseRenderer::GetPinCount()
  436. {
  437.     return 1;
  438. }
  439. // We only support one input pin and it is numbered zero
  440. CBasePin *CBaseRenderer::GetPin(int n)
  441. {
  442.     CAutoLock cObjectCreationLock(&m_ObjectCreationLock);
  443.     // Should only ever be called with zero
  444.     ASSERT(n == 0);
  445.     if (n != 0) {
  446.         return NULL;
  447.     }
  448.     // Create the input pin if not already done so
  449.     if (m_pInputPin == NULL) {
  450.         // hr must be initialized to NOERROR because
  451.         // CRendererInputPin's constructor only changes
  452.         // hr's value if an error occurs.
  453.         HRESULT hr = NOERROR;
  454.         m_pInputPin = new CRendererInputPin(this,&hr,L"In");
  455.         if (NULL == m_pInputPin) {
  456.             return NULL;
  457.         }
  458.         if (FAILED(hr)) {
  459.             delete m_pInputPin;
  460.             m_pInputPin = NULL;
  461.             return NULL;
  462.         }
  463.     }
  464.     return m_pInputPin;
  465. }
  466. // If "In" then return the IPin for our input pin, otherwise NULL and error
  467. STDMETHODIMP CBaseRenderer::FindPin(LPCWSTR Id, IPin **ppPin)
  468. {
  469.     CheckPointer(ppPin,E_POINTER);
  470.     if (0==lstrcmpW(Id,L"In")) {
  471.         *ppPin = GetPin(0);
  472.         ASSERT(*ppPin);
  473.         (*ppPin)->AddRef();
  474.     } else {
  475.         *ppPin = NULL;
  476.         return VFW_E_NOT_FOUND;
  477.     }
  478.     return NOERROR;
  479. }
  480. // Called when the input pin receives an EndOfStream notification. If we have
  481. // not got a sample, then notify EC_COMPLETE now. If we have samples, then set
  482. // m_bEOS and check for this on completing samples. If we're waiting to pause
  483. // then complete the transition to paused state by setting the state event
  484. HRESULT CBaseRenderer::EndOfStream()
  485. {
  486.     // Ignore these calls if we are stopped
  487.     if (m_State == State_Stopped) {
  488.         return NOERROR;
  489.     }
  490.     // If we have a sample then wait for it to be rendered
  491.     m_bEOS = TRUE;
  492.     if (m_pMediaSample) {
  493.         return NOERROR;
  494.     }
  495.     // If we are waiting for pause then we are now ready since we cannot now
  496.     // carry on waiting for a sample to arrive since we are being told there
  497.     // won't be any. This sets an event that the GetState function picks up
  498.     Ready();
  499.     // Only signal completion now if we are running otherwise queue it until
  500.     // we do run in StartStreaming. This is used when we seek because a seek
  501.     // causes a pause where early notification of completion is misleading
  502.     if (m_bStreaming) {
  503.         SendEndOfStream();
  504.     }
  505.     return NOERROR;
  506. }
  507. // When we are told to flush we should release the source thread
  508. HRESULT CBaseRenderer::BeginFlush()
  509. {
  510.     // If paused then report state intermediate until we get some data
  511.     if (m_State == State_Paused) {
  512.         NotReady();
  513.     }
  514.     SourceThreadCanWait(FALSE);
  515.     CancelNotification();
  516.     ClearPendingSample();
  517.     //  Wait for Receive to complete
  518.     WaitForReceiveToComplete();
  519.     return NOERROR;
  520. }
  521. // After flushing the source thread can wait in Receive again
  522. HRESULT CBaseRenderer::EndFlush()
  523. {
  524.     // Reset the current sample media time
  525.     if (m_pPosition) m_pPosition->ResetMediaTime();
  526.     // There should be no outstanding advise
  527.     ASSERT(CancelNotification() == S_FALSE);
  528.     SourceThreadCanWait(TRUE);
  529.     return NOERROR;
  530. }
  531. // We can now send EC_REPAINTs if so required
  532. HRESULT CBaseRenderer::CompleteConnect(IPin *pReceivePin)
  533. {
  534.     // The caller should always hold the interface lock because
  535.     // the function uses CBaseFilter::m_State.
  536.     ASSERT(CritCheckIn(&m_InterfaceLock));
  537.     m_bAbort = FALSE;
  538.     if (State_Running == GetRealState()) {
  539.         HRESULT hr = StartStreaming();
  540.         if (FAILED(hr)) {
  541.             return hr;
  542.         }
  543.         SetRepaintStatus(FALSE);
  544.     } else {
  545.         SetRepaintStatus(TRUE);
  546.     }
  547.     return NOERROR;
  548. }
  549. // Called when we go paused or running
  550. HRESULT CBaseRenderer::Active()
  551. {
  552.     return NOERROR;
  553. }
  554. // Called when we go into a stopped state
  555. HRESULT CBaseRenderer::Inactive()
  556. {
  557.     if (m_pPosition) {
  558.         m_pPosition->ResetMediaTime();
  559.     }
  560.     //  People who derive from this may want to override this behaviour
  561.     //  to keep hold of the sample in some circumstances
  562.     ClearPendingSample();
  563.     return NOERROR;
  564. }
  565. // Tell derived classes about the media type agreed
  566. HRESULT CBaseRenderer::SetMediaType(const CMediaType *pmt)
  567. {
  568.     return NOERROR;
  569. }
  570. // When we break the input pin connection we should reset the EOS flags. When
  571. // we are asked for either IMediaPosition or IMediaSeeking we will create a
  572. // CPosPassThru object to handles media time pass through. When we're handed
  573. // samples we store (by calling CPosPassThru::RegisterMediaTime) their media
  574. // times so we can then return a real current position of data being rendered
  575. HRESULT CBaseRenderer::BreakConnect()
  576. {
  577.     // Do we have a quality management sink
  578.     if (m_pQSink) {
  579.         m_pQSink->Release();
  580.         m_pQSink = NULL;
  581.     }
  582.     // Check we have a valid connection
  583.     if (m_pInputPin->IsConnected() == FALSE) {
  584.         return S_FALSE;
  585.     }
  586.     // Check we are stopped before disconnecting
  587.     if (m_State != State_Stopped && !m_pInputPin->CanReconnectWhenActive()) {
  588.         return VFW_E_NOT_STOPPED;
  589.     }
  590.     SetRepaintStatus(FALSE);
  591.     ResetEndOfStream();
  592.     ClearPendingSample();
  593.     m_bAbort = FALSE;
  594.     if (State_Running == m_State) {
  595.         StopStreaming();
  596.     }
  597.     return NOERROR;
  598. }
  599. // Retrieves the sample times for this samples (note the sample times are
  600. // passed in by reference not value). We return S_FALSE to say schedule this
  601. // sample according to the times on the sample. We also return S_OK in
  602. // which case the object should simply render the sample data immediately
  603. HRESULT CBaseRenderer::GetSampleTimes(IMediaSample *pMediaSample,
  604.                                       REFERENCE_TIME *pStartTime,
  605.                                       REFERENCE_TIME *pEndTime)
  606. {
  607.     ASSERT(m_dwAdvise == 0);
  608.     ASSERT(pMediaSample);
  609.     // If the stop time for this sample is before or the same as start time,
  610.     // then just ignore it (release it) and schedule the next one in line
  611.     // Source filters should always fill in the start and end times properly!
  612.     if (SUCCEEDED(pMediaSample->GetTime(pStartTime, pEndTime))) {
  613.         if (*pEndTime < *pStartTime) {
  614.             return VFW_E_START_TIME_AFTER_END;
  615.         }
  616.     } else {
  617.         // no time set in the sample... draw it now?
  618.         return S_OK;
  619.     }
  620.     // Can't synchronise without a clock so we return S_OK which tells the
  621.     // caller that the sample should be rendered immediately without going
  622.     // through the overhead of setting a timer advise link with the clock
  623.     if (m_pClock == NULL) {
  624.         return S_OK;
  625.     }
  626.     return ShouldDrawSampleNow(pMediaSample,pStartTime,pEndTime);
  627. }
  628. // By default all samples are drawn according to their time stamps so we
  629. // return S_FALSE. Returning S_OK means draw immediately, this is used
  630. // by the derived video renderer class in its quality management.
  631. HRESULT CBaseRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,
  632.                                            REFERENCE_TIME *ptrStart,
  633.                                            REFERENCE_TIME *ptrEnd)
  634. {
  635.     return S_FALSE;
  636. }
  637. // We must always reset the current advise time to zero after a timer fires
  638. // because there are several possible ways which lead us not to do any more
  639. // scheduling such as the pending image being cleared after state changes
  640. void CBaseRenderer::SignalTimerFired()
  641. {
  642.     m_dwAdvise = 0;
  643. }
  644. // Cancel any notification currently scheduled. This is called by the owning
  645. // window object when it is told to stop streaming. If there is no timer link
  646. // outstanding then calling this is benign otherwise we go ahead and cancel
  647. // We must always reset the render event as the quality management code can
  648. // signal immediate rendering by setting the event without setting an advise
  649. // link. If we're subsequently stopped and run the first attempt to setup an
  650. // advise link with the reference clock will find the event still signalled
  651. HRESULT CBaseRenderer::CancelNotification()
  652. {
  653.     ASSERT(m_dwAdvise == 0 || m_pClock);
  654.     DWORD_PTR dwAdvise = m_dwAdvise;
  655.     // Have we a live advise link
  656.     if (m_dwAdvise) {
  657.         m_pClock->Unadvise(m_dwAdvise);
  658.         SignalTimerFired();
  659.         ASSERT(m_dwAdvise == 0);
  660.     }
  661.     // Clear the event and return our status
  662.     m_RenderEvent.Reset();
  663.     return (dwAdvise ? S_OK : S_FALSE);
  664. }
  665. // Responsible for setting up one shot advise links with the clock
  666. // Return FALSE if the sample is to be dropped (not drawn at all)
  667. // Return TRUE if the sample is to be drawn and in this case also
  668. // arrange for m_RenderEvent to be set at the appropriate time
  669. BOOL CBaseRenderer::ScheduleSample(IMediaSample *pMediaSample)
  670. {
  671.     REFERENCE_TIME StartSample, EndSample;
  672.     // Is someone pulling our leg
  673.     if (pMediaSample == NULL) {
  674.         return FALSE;
  675.     }
  676.     // Get the next sample due up for rendering.  If there aren't any ready
  677.     // then GetNextSampleTimes returns an error.  If there is one to be done
  678.     // then it succeeds and yields the sample times. If it is due now then
  679.     // it returns S_OK other if it's to be done when due it returns S_FALSE
  680.     HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample);
  681.     if (FAILED(hr)) {
  682.         return FALSE;
  683.     }
  684.     // If we don't have a reference clock then we cannot set up the advise
  685.     // time so we simply set the event indicating an image to render. This
  686.     // will cause us to run flat out without any timing or synchronisation
  687.     if (hr == S_OK) {
  688.         EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent));
  689.         return TRUE;
  690.     }
  691.     ASSERT(m_dwAdvise == 0);
  692.     ASSERT(m_pClock);
  693.     ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
  694.     // We do have a valid reference clock interface so we can ask it to
  695.     // set an event when the image comes due for rendering. We pass in
  696.     // the reference time we were told to start at and also the current
  697.     // stream time which is the offset from the start reference time
  698.     hr = m_pClock->AdviseTime(
  699.             (REFERENCE_TIME) m_tStart,          // Start run time
  700.             StartSample,                        // Stream time
  701.             (HEVENT)(HANDLE) m_RenderEvent,     // Render notification
  702.             &m_dwAdvise);                       // Advise cookie
  703.     if (SUCCEEDED(hr)) {
  704.         return TRUE;
  705.     }
  706.     // We could not schedule the next sample for rendering despite the fact
  707.     // we have a valid sample here. This is a fair indication that either
  708.     // the system clock is wrong or the time stamp for the sample is duff
  709.     ASSERT(m_dwAdvise == 0);
  710.     return FALSE;
  711. }
  712. // This is called when a sample comes due for rendering. We pass the sample
  713. // on to the derived class. After rendering we will initialise the timer for
  714. // the next sample, NOTE signal that the last one fired first, if we don't
  715. // do this it thinks there is still one outstanding that hasn't completed
  716. HRESULT CBaseRenderer::Render(IMediaSample *pMediaSample)
  717. {
  718.     // If the media sample is NULL then we will have been notified by the
  719.     // clock that another sample is ready but in the mean time someone has
  720.     // stopped us streaming which causes the next sample to be released
  721.     if (pMediaSample == NULL) {
  722.         return S_FALSE;
  723.     }
  724.     // If we have stopped streaming then don't render any more samples, the
  725.     // thread that got in and locked us and then reset this flag does not
  726.     // clear the pending sample as we can use it to refresh any output device
  727.     if (m_bStreaming == FALSE) {
  728.         return S_FALSE;
  729.     }
  730.     // Time how long the rendering takes
  731.     OnRenderStart(pMediaSample);
  732.     DoRenderSample(pMediaSample);
  733.     OnRenderEnd(pMediaSample);
  734.     return NOERROR;
  735. }
  736. // Checks if there is a sample waiting at the renderer
  737. BOOL CBaseRenderer::HaveCurrentSample()
  738. {
  739.     CAutoLock cRendererLock(&m_RendererLock);
  740.     return (m_pMediaSample == NULL ? FALSE : TRUE);
  741. }
  742. // Returns the current sample waiting at the video renderer. We AddRef the
  743. // sample before returning so that should it come due for rendering the
  744. // person who called this method will hold the remaining reference count
  745. // that will stop the sample being added back onto the allocator free list
  746. IMediaSample *CBaseRenderer::GetCurrentSample()
  747. {
  748.     CAutoLock cRendererLock(&m_RendererLock);
  749.     if (m_pMediaSample) {
  750.         m_pMediaSample->AddRef();
  751.     }
  752.     return m_pMediaSample;
  753. }
  754. // Called when the source delivers us a sample. We go through a few checks to
  755. // make sure the sample can be rendered. If we are running (streaming) then we
  756. // have the sample scheduled with the reference clock, if we are not streaming
  757. // then we have received an sample in paused mode so we can complete any state
  758. // transition. On leaving this function everything will be unlocked so an app
  759. // thread may get in and change our state to stopped (for example) in which
  760. // case it will also signal the thread event so that our wait call is stopped
  761. HRESULT CBaseRenderer::PrepareReceive(IMediaSample *pMediaSample)
  762. {
  763.     CAutoLock cInterfaceLock(&m_InterfaceLock);
  764.     m_bInReceive = TRUE;
  765.     // Check our flushing and filter state
  766.     // This function must hold the interface lock because it calls 
  767.     // CBaseInputPin::Receive() and CBaseInputPin::Receive() uses
  768.     // CBasePin::m_bRunTimeError.
  769.     HRESULT hr = m_pInputPin->CBaseInputPin::Receive(pMediaSample);
  770.     if (hr != NOERROR) {
  771.         m_bInReceive = FALSE;
  772.         return E_FAIL;
  773.     }
  774.     // Has the type changed on a media sample. We do all rendering
  775.     // synchronously on the source thread, which has a side effect
  776.     // that only one buffer is ever outstanding. Therefore when we
  777.     // have Receive called we can go ahead and change the format
  778.     // Since the format change can cause a SendMessage we just don't
  779.     // lock
  780.     if (m_pInputPin->SampleProps()->pMediaType) {
  781.         hr = m_pInputPin->SetMediaType(
  782.                 (CMediaType *)m_pInputPin->SampleProps()->pMediaType);
  783.         if (FAILED(hr)) {
  784.             m_bInReceive = FALSE;
  785.             return hr;
  786.         }
  787.     }
  788.     CAutoLock cSampleLock(&m_RendererLock);
  789.     ASSERT(IsActive() == TRUE);
  790.     ASSERT(m_pInputPin->IsFlushing() == FALSE);
  791.     ASSERT(m_pInputPin->IsConnected() == TRUE);
  792.     ASSERT(m_pMediaSample == NULL);
  793.     // Return an error if we already have a sample waiting for rendering
  794.     // source pins must serialise the Receive calls - we also check that
  795.     // no data is being sent after the source signalled an end of stream
  796.     if (m_pMediaSample || m_bEOS || m_bAbort) {
  797.         Ready();
  798.         m_bInReceive = FALSE;
  799.         return E_UNEXPECTED;
  800.     }
  801.     // Store the media times from this sample
  802.     if (m_pPosition) m_pPosition->RegisterMediaTime(pMediaSample);
  803.     // Schedule the next sample if we are streaming
  804.     if ((m_bStreaming == TRUE) && (ScheduleSample(pMediaSample) == FALSE)) {
  805.         ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
  806.         ASSERT(CancelNotification() == S_FALSE);
  807.         m_bInReceive = FALSE;
  808.         return VFW_E_SAMPLE_REJECTED;
  809.     }
  810.     // Store the sample end time for EC_COMPLETE handling
  811.     m_SignalTime = m_pInputPin->SampleProps()->tStop;
  812.     // BEWARE we sometimes keep the sample even after returning the thread to
  813.     // the source filter such as when we go into a stopped state (we keep it
  814.     // to refresh the device with) so we must AddRef it to keep it safely. If
  815.     // we start flushing the source thread is released and any sample waiting
  816.     // will be released otherwise GetBuffer may never return (see BeginFlush)
  817.     m_pMediaSample = pMediaSample;
  818.     m_pMediaSample->AddRef();
  819.     if (m_bStreaming == FALSE) {
  820.         SetRepaintStatus(TRUE);
  821.     }
  822.     return NOERROR;
  823. }
  824. // Called by the source filter when we have a sample to render. Under normal
  825. // circumstances we set an advise link with the clock, wait for the time to
  826. // arrive and then render the data using the PURE virtual DoRenderSample that
  827. // the derived class will have overriden. After rendering the sample we may
  828. // also signal EOS if it was the last one sent before EndOfStream was called
  829. HRESULT CBaseRenderer::Receive(IMediaSample *pSample)
  830. {
  831.     ASSERT(pSample);
  832.     // It may return VFW_E_SAMPLE_REJECTED code to say don't bother
  833.     HRESULT hr = PrepareReceive(pSample);
  834.     ASSERT(m_bInReceive == SUCCEEDED(hr));
  835.     if (FAILED(hr)) {
  836.         if (hr == VFW_E_SAMPLE_REJECTED) {
  837.             return NOERROR;
  838.         }
  839.         return hr;
  840.     }
  841.     // We realize the palette in "PrepareRender()" so we have to give away the
  842.     // filter lock here.
  843.     if (m_State == State_Paused) {
  844.         PrepareRender();
  845.         // no need to use InterlockedExchange
  846.         m_bInReceive = FALSE;
  847.         {
  848.             // We must hold both these locks
  849.             CAutoLock cRendererLock(&m_InterfaceLock);
  850.             if (m_State == State_Stopped)
  851.                 return NOERROR;
  852.             m_bInReceive = TRUE;
  853.             CAutoLock cSampleLock(&m_RendererLock);
  854.             OnReceiveFirstSample(pSample);
  855.         }
  856.         Ready();
  857.     }
  858.     // Having set an advise link with the clock we sit and wait. We may be
  859.     // awoken by the clock firing or by a state change. The rendering call
  860.     // will lock the critical section and check we can still render the data
  861.     hr = WaitForRenderTime();
  862.     if (FAILED(hr)) {
  863.         m_bInReceive = FALSE;
  864.         return NOERROR;
  865.     }
  866.     PrepareRender();
  867.     //  Set this here and poll it until we work out the locking correctly
  868.     //  It can't be right that the streaming stuff grabs the interface
  869.     //  lock - after all we want to be able to wait for this stuff
  870.     //  to complete
  871.     m_bInReceive = FALSE;
  872.     // We must hold both these locks
  873.     CAutoLock cRendererLock(&m_InterfaceLock);
  874.     // since we gave away the filter wide lock, the sate of the filter could
  875.     // have chnaged to Stopped
  876.     if (m_State == State_Stopped)
  877.         return NOERROR;
  878.     CAutoLock cSampleLock(&m_RendererLock);
  879.     // Deal with this sample
  880.     Render(m_pMediaSample);
  881.     ClearPendingSample();
  882.     SendEndOfStream();
  883.     CancelNotification();
  884.     return NOERROR;
  885. }
  886. // This is called when we stop or are inactivated to clear the pending sample
  887. // We release the media sample interface so that they can be allocated to the
  888. // source filter again, unless of course we are changing state to inactive in
  889. // which case GetBuffer will return an error. We must also reset the current
  890. // media sample to NULL so that we know we do not currently have an image
  891. HRESULT CBaseRenderer::ClearPendingSample()
  892. {
  893.     CAutoLock cRendererLock(&m_RendererLock);
  894.     if (m_pMediaSample) {
  895.         m_pMediaSample->Release();
  896.         m_pMediaSample = NULL;
  897.     }
  898.     return NOERROR;
  899. }
  900. // Used to signal end of stream according to the sample end time
  901. void CALLBACK EndOfStreamTimer(UINT uID,        // Timer identifier
  902.                                UINT uMsg,       // Not currently used
  903.                                DWORD_PTR dwUser,// User information
  904.                                DWORD_PTR dw1,   // Windows reserved
  905.                                DWORD_PTR dw2)   // is also reserved
  906. {
  907.     CBaseRenderer *pRenderer = (CBaseRenderer *) dwUser;
  908.     NOTE1("EndOfStreamTimer called (%d)",uID);
  909.     pRenderer->TimerCallback();
  910. }
  911. //  Do the timer callback work
  912. void CBaseRenderer::TimerCallback()
  913. {
  914.     //  Lock for synchronization (but don't hold this lock when calling
  915.     //  timeKillEvent)
  916.     CAutoLock cRendererLock(&m_RendererLock);
  917.     // See if we should signal end of stream now
  918.     if (m_EndOfStreamTimer) {
  919.         m_EndOfStreamTimer = 0;
  920.         SendEndOfStream();
  921.     }
  922. }
  923. // If we are at the end of the stream signal the filter graph but do not set
  924. // the state flag back to FALSE. Once we drop off the end of the stream we
  925. // leave the flag set (until a subsequent ResetEndOfStream). Each sample we
  926. // get delivered will update m_SignalTime to be the last sample's end time.
  927. // We must wait this long before signalling end of stream to the filtergraph
  928. #define TIMEOUT_DELIVERYWAIT 50
  929. #define TIMEOUT_RESOLUTION 10
  930. HRESULT CBaseRenderer::SendEndOfStream()
  931. {
  932.     ASSERT(CritCheckIn(&m_RendererLock));
  933.     if (m_bEOS == FALSE || m_bEOSDelivered || m_EndOfStreamTimer) {
  934.         return NOERROR;
  935.     }
  936.     // If there is no clock then signal immediately
  937.     if (m_pClock == NULL) {
  938.         return NotifyEndOfStream();
  939.     }
  940.     // How long into the future is the delivery time
  941.     REFERENCE_TIME Signal = m_tStart + m_SignalTime;
  942.     REFERENCE_TIME CurrentTime;
  943.     m_pClock->GetTime(&CurrentTime);
  944.     LONG Delay = LONG((Signal - CurrentTime) / 10000);
  945.     // Dump the timing information to the debugger
  946.     NOTE1("Delay until end of stream delivery %d",Delay);
  947.     NOTE1("Current %s",(LPCTSTR)CDisp((LONGLONG)CurrentTime));
  948.     NOTE1("Signal %s",(LPCTSTR)CDisp((LONGLONG)Signal));
  949.     // Wait for the delivery time to arrive
  950.     if (Delay < TIMEOUT_DELIVERYWAIT) {
  951.         return NotifyEndOfStream();
  952.     }
  953.     // Signal a timer callback on another worker thread
  954.     m_EndOfStreamTimer = CompatibleTimeSetEvent((UINT) Delay, // Period of timer
  955.                                       TIMEOUT_RESOLUTION,     // Timer resolution
  956.                                       EndOfStreamTimer,       // Callback function
  957.                                       DWORD_PTR(this),        // Used information
  958.                                       TIME_ONESHOT);          // Type of callback
  959.     if (m_EndOfStreamTimer == 0) {
  960.         return NotifyEndOfStream();
  961.     }
  962.     return NOERROR;
  963. }
  964. // Signals EC_COMPLETE to the filtergraph manager
  965. HRESULT CBaseRenderer::NotifyEndOfStream()
  966. {
  967.     CAutoLock cRendererLock(&m_RendererLock);
  968.     ASSERT(m_bEOSDelivered == FALSE);
  969.     ASSERT(m_EndOfStreamTimer == 0);
  970.     // Has the filter changed state
  971.     if (m_bStreaming == FALSE) {
  972.         ASSERT(m_EndOfStreamTimer == 0);
  973.         return NOERROR;
  974.     }
  975.     // Reset the end of stream timer
  976.     m_EndOfStreamTimer = 0;
  977.     // If we've been using the IMediaPosition interface, set it's start
  978.     // and end media "times" to the stop position by hand.  This ensures
  979.     // that we actually get to the end, even if the MPEG guestimate has
  980.     // been bad or if the quality management dropped the last few frames
  981.     if (m_pPosition) m_pPosition->EOS();
  982.     m_bEOSDelivered = TRUE;
  983.     NOTE("Sending EC_COMPLETE...");
  984.     return NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);
  985. }
  986. // Reset the end of stream flag, this is typically called when we transfer to
  987. // stopped states since that resets the current position back to the start so
  988. // we will receive more samples or another EndOfStream if there aren't any. We
  989. // keep two separate flags one to say we have run off the end of the stream
  990. // (this is the m_bEOS flag) and another to say we have delivered EC_COMPLETE
  991. // to the filter graph. We need the latter otherwise we can end up sending an
  992. // EC_COMPLETE every time the source changes state and calls our EndOfStream
  993. HRESULT CBaseRenderer::ResetEndOfStream()
  994. {
  995.     ResetEndOfStreamTimer();
  996.     CAutoLock cRendererLock(&m_RendererLock);
  997.     m_bEOS = FALSE;
  998.     m_bEOSDelivered = FALSE;
  999.     m_SignalTime = 0;
  1000.     return NOERROR;
  1001. }
  1002. // Kills any outstanding end of stream timer
  1003. void CBaseRenderer::ResetEndOfStreamTimer()
  1004. {
  1005.     ASSERT(CritCheckOut(&m_RendererLock));
  1006.     if (m_EndOfStreamTimer) {
  1007.         timeKillEvent(m_EndOfStreamTimer);
  1008.         m_EndOfStreamTimer = 0;
  1009.     }
  1010. }
  1011. // This is called when we start running so that we can schedule any pending
  1012. // image we have with the clock and display any timing information. If we
  1013. // don't have any sample but we have queued an EOS flag then we send it. If
  1014. // we do have a sample then we wait until that has been rendered before we
  1015. // signal the filter graph otherwise we may change state before it's done
  1016. HRESULT CBaseRenderer::StartStreaming()
  1017. {
  1018.     CAutoLock cRendererLock(&m_RendererLock);
  1019.     if (m_bStreaming == TRUE) {
  1020.         return NOERROR;
  1021.     }
  1022.     // Reset the streaming times ready for running
  1023.     m_bStreaming = TRUE;
  1024.     timeBeginPeriod(1);
  1025.     OnStartStreaming();
  1026.     // There should be no outstanding advise
  1027.     ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));
  1028.     ASSERT(CancelNotification() == S_FALSE);
  1029.     // If we have an EOS and no data then deliver it now
  1030.     if (m_pMediaSample == NULL) {
  1031.         return SendEndOfStream();
  1032.     }
  1033.     // Have the data rendered
  1034.     ASSERT(m_pMediaSample);
  1035.     if (!ScheduleSample(m_pMediaSample))
  1036.         m_RenderEvent.Set();
  1037.     return NOERROR;
  1038. }
  1039. // This is called when we stop streaming so that we can set our internal flag
  1040. // indicating we are not now to schedule any more samples arriving. The state
  1041. // change methods in the filter implementation take care of cancelling any
  1042. // clock advise link we have set up and clearing any pending sample we have
  1043. HRESULT CBaseRenderer::StopStreaming()
  1044. {
  1045.     CAutoLock cRendererLock(&m_RendererLock);
  1046.     m_bEOSDelivered = FALSE;
  1047.     if (m_bStreaming == TRUE) {
  1048.         m_bStreaming = FALSE;
  1049.         OnStopStreaming();
  1050.         timeEndPeriod(1);
  1051.     }
  1052.     return NOERROR;
  1053. }
  1054. // We have a boolean flag that is reset when we have signalled EC_REPAINT to
  1055. // the filter graph. We set this when we receive an image so that should any
  1056. // conditions arise again we can send another one. By having a flag we ensure
  1057. // we don't flood the filter graph with redundant calls. We do not set the
  1058. // event when we receive an EndOfStream call since there is no point in us
  1059. // sending further EC_REPAINTs. In particular the AutoShowWindow method and
  1060. // the DirectDraw object use this method to control the window repainting
  1061. void CBaseRenderer::SetRepaintStatus(BOOL bRepaint)
  1062. {
  1063.     CAutoLock cSampleLock(&m_RendererLock);
  1064.     m_bRepaintStatus = bRepaint;
  1065. }
  1066. // Pass the window handle to the upstream filter
  1067. void CBaseRenderer::SendNotifyWindow(IPin *pPin,HWND hwnd)
  1068. {
  1069.     IMediaEventSink *pSink;
  1070.     // Does the pin support IMediaEventSink
  1071.     HRESULT hr = pPin->QueryInterface(IID_IMediaEventSink,(void **)&pSink);
  1072.     if (SUCCEEDED(hr)) {
  1073.         pSink->Notify(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
  1074.         pSink->Release();
  1075.     }
  1076.     NotifyEvent(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);
  1077. }
  1078. // Signal an EC_REPAINT to the filter graph. This can be used to have data
  1079. // sent to us. For example when a video window is first displayed it may
  1080. // not have an image to display, at which point it signals EC_REPAINT. The
  1081. // filtergraph will either pause the graph if stopped or if already paused
  1082. // it will call put_CurrentPosition of the current position. Setting the
  1083. // current position to itself has the stream flushed and the image resent
  1084. #define RLOG(_x_) DbgLog((LOG_TRACE,1,TEXT(_x_)));
  1085. void CBaseRenderer::SendRepaint()
  1086. {
  1087.     CAutoLock cSampleLock(&m_RendererLock);
  1088.     ASSERT(m_pInputPin);
  1089.     // We should not send repaint notifications when...
  1090.     //    - An end of stream has been notified
  1091.     //    - Our input pin is being flushed
  1092.     //    - The input pin is not connected
  1093.     //    - We have aborted a video playback
  1094.     //    - There is a repaint already sent
  1095.     if (m_bAbort == FALSE) {
  1096.         if (m_pInputPin->IsConnected() == TRUE) {
  1097.             if (m_pInputPin->IsFlushing() == FALSE) {
  1098.                 if (IsEndOfStream() == FALSE) {
  1099.                     if (m_bRepaintStatus == TRUE) {
  1100.                         IPin *pPin = (IPin *) m_pInputPin;
  1101.                         NotifyEvent(EC_REPAINT,(LONG_PTR) pPin,0);
  1102.                         SetRepaintStatus(FALSE);
  1103.                         RLOG("Sending repaint");
  1104.                     }
  1105.                 }
  1106.             }
  1107.         }
  1108.     }
  1109. }
  1110. // When a video window detects a display change (WM_DISPLAYCHANGE message) it
  1111. // can send an EC_DISPLAY_CHANGED event code along with the renderer pin. The
  1112. // filtergraph will stop everyone and reconnect our input pin. As we're then
  1113. // reconnected we can accept the media type that matches the new display mode
  1114. // since we may no longer be able to draw the current image type efficiently
  1115. BOOL CBaseRenderer::OnDisplayChange()
  1116. {
  1117.     // Ignore if we are not connected yet
  1118.     CAutoLock cSampleLock(&m_RendererLock);
  1119.     if (m_pInputPin->IsConnected() == FALSE) {
  1120.         return FALSE;
  1121.     }
  1122.     RLOG("Notification of EC_DISPLAY_CHANGE");
  1123.     // Pass our input pin as parameter on the event
  1124.     IPin *pPin = (IPin *) m_pInputPin;
  1125.     m_pInputPin->AddRef();
  1126.     NotifyEvent(EC_DISPLAY_CHANGED,(LONG_PTR) pPin,0);
  1127.     SetAbortSignal(TRUE);
  1128.     ClearPendingSample();
  1129.     m_pInputPin->Release();
  1130.     return TRUE;
  1131. }
  1132. // Called just before we start drawing.
  1133. // Store the current time in m_trRenderStart to allow the rendering time to be
  1134. // logged.  Log the time stamp of the sample and how late it is (neg is early)
  1135. void CBaseRenderer::OnRenderStart(IMediaSample *pMediaSample)
  1136. {
  1137. #ifdef PERF
  1138.     REFERENCE_TIME trStart, trEnd;
  1139.     pMediaSample->GetTime(&trStart, &trEnd);
  1140.     MSR_INTEGER(m_idBaseStamp, (int)trStart);     // dump low order 32 bits
  1141.     m_pClock->GetTime(&m_trRenderStart);
  1142.     MSR_INTEGER(0, (int)m_trRenderStart);
  1143.     REFERENCE_TIME trStream;
  1144.     trStream = m_trRenderStart-m_tStart;     // convert reftime to stream time
  1145.     MSR_INTEGER(0,(int)trStream);
  1146.     const int trLate = (int)(trStream - trStart);
  1147.     MSR_INTEGER(m_idBaseAccuracy, trLate/10000);  // dump in mSec
  1148. #endif
  1149. } // OnRenderStart
  1150. // Called directly after drawing an image.
  1151. // calculate the time spent drawing and log it.
  1152. void CBaseRenderer::OnRenderEnd(IMediaSample *pMediaSample)
  1153. {
  1154. #ifdef PERF
  1155.     REFERENCE_TIME trNow;
  1156.     m_pClock->GetTime(&trNow);
  1157.     MSR_INTEGER(0,(int)trNow);
  1158.     int t = (int)((trNow - m_trRenderStart)/10000);   // convert UNITS->msec
  1159.     MSR_INTEGER(m_idBaseRenderTime, t);
  1160. #endif
  1161. } // OnRenderEnd
  1162. // Constructor must be passed the base renderer object
  1163. CRendererInputPin::CRendererInputPin(CBaseRenderer *pRenderer,
  1164.                                      HRESULT *phr,
  1165.                                      LPCWSTR pPinName) :
  1166.     CBaseInputPin(NAME("Renderer pin"),
  1167.                   pRenderer,
  1168.                   &pRenderer->m_InterfaceLock,
  1169.                   (HRESULT *) phr,
  1170.                   pPinName)
  1171. {
  1172.     m_pRenderer = pRenderer;
  1173.     ASSERT(m_pRenderer);
  1174. }
  1175. // Signals end of data stream on the input pin
  1176. STDMETHODIMP CRendererInputPin::EndOfStream()
  1177. {
  1178.     CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
  1179.     CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
  1180.     // Make sure we're streaming ok
  1181.     HRESULT hr = CheckStreaming();
  1182.     if (hr != NOERROR) {
  1183.         return hr;
  1184.     }
  1185.     // Pass it onto the renderer
  1186.     hr = m_pRenderer->EndOfStream();
  1187.     if (SUCCEEDED(hr)) {
  1188.         hr = CBaseInputPin::EndOfStream();
  1189.     }
  1190.     return hr;
  1191. }
  1192. // Signals start of flushing on the input pin - we do the final reset end of
  1193. // stream with the renderer lock unlocked but with the interface lock locked
  1194. // We must do this because we call timeKillEvent, our timer callback method
  1195. // has to take the renderer lock to serialise our state. Therefore holding a
  1196. // renderer lock when calling timeKillEvent could cause a deadlock condition
  1197. STDMETHODIMP CRendererInputPin::BeginFlush()
  1198. {
  1199.     CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
  1200.     {
  1201.         CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
  1202.         CBaseInputPin::BeginFlush();
  1203.         m_pRenderer->BeginFlush();
  1204.     }
  1205.     return m_pRenderer->ResetEndOfStream();
  1206. }
  1207. // Signals end of flushing on the input pin
  1208. STDMETHODIMP CRendererInputPin::EndFlush()
  1209. {
  1210.     CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
  1211.     CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);
  1212.     HRESULT hr = m_pRenderer->EndFlush();
  1213.     if (SUCCEEDED(hr)) {
  1214.         hr = CBaseInputPin::EndFlush();
  1215.     }
  1216.     return hr;
  1217. }
  1218. // Pass the sample straight through to the renderer object
  1219. STDMETHODIMP CRendererInputPin::Receive(IMediaSample *pSample)
  1220. {
  1221.     HRESULT hr = m_pRenderer->Receive(pSample);
  1222.     if (FAILED(hr)) {
  1223.         // A deadlock could occur if the caller holds the renderer lock and
  1224.         // attempts to acquire the interface lock.
  1225.         ASSERT(CritCheckOut(&m_pRenderer->m_RendererLock));
  1226.         {
  1227.             // The interface lock must be held when the filter is calling
  1228.             // IsStopped() or IsFlushing().  The interface lock must also
  1229.             // be held because the function uses m_bRunTimeError.
  1230.             CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);
  1231.             // We do not report errors which occur while the filter is stopping,
  1232.             // flushing or if the m_bAbort flag is set .  Errors are expected to 
  1233.             // occur during these operations and the streaming thread correctly 
  1234.             // handles the errors.  
  1235.             if (!IsStopped() && !IsFlushing() && !m_pRenderer->m_bAbort && !m_bRunTimeError) {
  1236.                 // EC_ERRORABORT's first parameter is the error which caused
  1237.                 // the event and its' last parameter is 0.  See the Direct
  1238.                 // Show SDK documentation for more information.
  1239.                 m_pRenderer->NotifyEvent(EC_ERRORABORT,hr,0);
  1240.                 {
  1241.                     CAutoLock alRendererLock(&m_pRenderer->m_RendererLock);
  1242.                     if (m_pRenderer->IsStreaming() && !m_pRenderer->IsEndOfStreamDelivered()) {
  1243.                         m_pRenderer->NotifyEndOfStream();
  1244.                     }
  1245.                 }
  1246.     
  1247.                 m_bRunTimeError = TRUE;
  1248.             }
  1249.         }
  1250.     }
  1251.     return hr;
  1252. }
  1253. // Called when the input pin is disconnected
  1254. HRESULT CRendererInputPin::BreakConnect()
  1255. {
  1256.     HRESULT hr = m_pRenderer->BreakConnect();
  1257.     if (FAILED(hr)) {
  1258.         return hr;
  1259.     }
  1260.     return CBaseInputPin::BreakConnect();
  1261. }
  1262. // Called when the input pin is connected
  1263. HRESULT CRendererInputPin::CompleteConnect(IPin *pReceivePin)
  1264. {
  1265.     HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);
  1266.     if (FAILED(hr)) {
  1267.         return hr;
  1268.     }
  1269.     return CBaseInputPin::CompleteConnect(pReceivePin);
  1270. }
  1271. // Give the pin id of our one and only pin
  1272. STDMETHODIMP CRendererInputPin::QueryId(LPWSTR *Id)
  1273. {
  1274.     CheckPointer(Id,E_POINTER);
  1275.     const size_t len = 4;
  1276.     *Id = (LPWSTR)CoTaskMemAlloc(len * sizeof(WCHAR));
  1277.     if (*Id == NULL) {
  1278.         return E_OUTOFMEMORY;
  1279.     }
  1280.     (void)StringCchCopyW(*Id, len, L"In");
  1281.     return NOERROR;
  1282. }
  1283. // Will the filter accept this media type
  1284. HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)
  1285. {
  1286.     return m_pRenderer->CheckMediaType(pmt);
  1287. }
  1288. // Called when we go paused or running
  1289. HRESULT CRendererInputPin::Active()
  1290. {
  1291.     return m_pRenderer->Active();
  1292. }
  1293. // Called when we go into a stopped state
  1294. HRESULT CRendererInputPin::Inactive()
  1295. {
  1296.     // The caller must hold the interface lock because 
  1297.     // this function uses m_bRunTimeError.
  1298.     ASSERT(CritCheckIn(&m_pRenderer->m_InterfaceLock));
  1299.     m_bRunTimeError = FALSE;
  1300.     return m_pRenderer->Inactive();
  1301. }
  1302. // Tell derived classes about the media type agreed
  1303. HRESULT CRendererInputPin::SetMediaType(const CMediaType *pmt)
  1304. {
  1305.     HRESULT hr = CBaseInputPin::SetMediaType(pmt);
  1306.     if (FAILED(hr)) {
  1307.         return hr;
  1308.     }
  1309.     return m_pRenderer->SetMediaType(pmt);
  1310. }
  1311. // We do not keep an event object to use when setting up a timer link with
  1312. // the clock but are given a pointer to one by the owning object through the
  1313. // SetNotificationObject method - this must be initialised before starting
  1314. // We can override the default quality management process to have it always
  1315. // draw late frames, this is currently done by having the following registry
  1316. // key (actually an INI key) called DrawLateFrames set to 1 (default is 0)
  1317. const TCHAR AMQUALITY[] = TEXT("ActiveMovie");
  1318. const TCHAR DRAWLATEFRAMES[] = TEXT("DrawLateFrames");
  1319. CBaseVideoRenderer::CBaseVideoRenderer(
  1320.       REFCLSID RenderClass, // CLSID for this renderer
  1321.       TCHAR *pName,         // Debug ONLY description
  1322.       LPUNKNOWN pUnk,       // Aggregated owner object
  1323.       HRESULT *phr) :       // General OLE return code
  1324.     CBaseRenderer(RenderClass,pName,pUnk,phr),
  1325.     m_cFramesDropped(0),
  1326.     m_cFramesDrawn(0),
  1327.     m_bSupplierHandlingQuality(FALSE)
  1328. {
  1329.     ResetStreamingTimes();
  1330. #ifdef PERF
  1331.     m_idTimeStamp       = MSR_REGISTER(TEXT("Frame time stamp"));
  1332.     m_idEarliness       = MSR_REGISTER(TEXT("Earliness fudge"));
  1333.     m_idTarget          = MSR_REGISTER(TEXT("Target (mSec)"));
  1334.     m_idSchLateTime     = MSR_REGISTER(TEXT("mSec late when scheduled"));
  1335.     m_idDecision        = MSR_REGISTER(TEXT("Scheduler decision code"));
  1336.     m_idQualityRate     = MSR_REGISTER(TEXT("Quality rate sent"));
  1337.     m_idQualityTime     = MSR_REGISTER(TEXT("Quality time sent"));
  1338.     m_idWaitReal        = MSR_REGISTER(TEXT("Render wait"));
  1339.     // m_idWait            = MSR_REGISTER(TEXT("wait time recorded (msec)"));
  1340.     m_idFrameAccuracy   = MSR_REGISTER(TEXT("Frame accuracy (msecs)"));
  1341.     m_bDrawLateFrames = GetProfileInt(AMQUALITY, DRAWLATEFRAMES, FALSE);
  1342.     //m_idSendQuality      = MSR_REGISTER(TEXT("Processing Quality message"));
  1343.     m_idRenderAvg       = MSR_REGISTER(TEXT("Render draw time Avg"));
  1344.     m_idFrameAvg        = MSR_REGISTER(TEXT("FrameAvg"));
  1345.     m_idWaitAvg         = MSR_REGISTER(TEXT("WaitAvg"));
  1346.     m_idDuration        = MSR_REGISTER(TEXT("Duration"));
  1347.     m_idThrottle        = MSR_REGISTER(TEXT("Audio-video throttle wait"));
  1348.     // m_idDebug           = MSR_REGISTER(TEXT("Debug stuff"));
  1349. #endif // PERF
  1350. } // Constructor
  1351. // Destructor is just a placeholder
  1352. CBaseVideoRenderer::~CBaseVideoRenderer()
  1353. {
  1354.     ASSERT(m_dwAdvise == 0);
  1355. }
  1356. // The timing functions in this class are called by the window object and by
  1357. // the renderer's allocator.
  1358. // The windows object calls timing functions as it receives media sample
  1359. // images for drawing using GDI.
  1360. // The allocator calls timing functions when it starts passing DCI/DirectDraw
  1361. // surfaces which are not rendered in the same way; The decompressor writes
  1362. // directly to the surface with no separate rendering, so those code paths
  1363. // call direct into us.  Since we only ever hand out DCI/DirectDraw surfaces
  1364. // when we have allocated one and only one image we know there cannot be any
  1365. // conflict between the two.
  1366. //
  1367. // We use timeGetTime to return the timing counts we use (since it's relative
  1368. // performance we are interested in rather than absolute compared to a clock)
  1369. // The window object sets the accuracy of the system clock (normally 1ms) by
  1370. // calling timeBeginPeriod/timeEndPeriod when it changes streaming states
  1371. // Reset all times controlling streaming.
  1372. // Set them so that
  1373. // 1. Frames will not initially be dropped
  1374. // 2. The first frame will definitely be drawn (achieved by saying that there
  1375. //    has not ben a frame drawn for a long time).
  1376. HRESULT CBaseVideoRenderer::ResetStreamingTimes()
  1377. {
  1378.     m_trLastDraw = -1000;     // set up as first frame since ages (1 sec) ago
  1379.     m_tStreamingStart = timeGetTime();
  1380.     m_trRenderAvg = 0;
  1381.     m_trFrameAvg = -1;        // -1000 fps == "unset"
  1382.     m_trDuration = 0;         // 0 - strange value
  1383.     m_trRenderLast = 0;
  1384.     m_trWaitAvg = 0;
  1385.     m_tRenderStart = 0;
  1386.     m_cFramesDrawn = 0;
  1387.     m_cFramesDropped = 0;
  1388.     m_iTotAcc = 0;
  1389.     m_iSumSqAcc = 0;
  1390.     m_iSumSqFrameTime = 0;
  1391.     m_trFrame = 0;          // hygiene - not really needed
  1392.     m_trLate = 0;           // hygiene - not really needed
  1393.     m_iSumFrameTime = 0;
  1394.     m_nNormal = 0;
  1395.     m_trEarliness = 0;
  1396.     m_trTarget = -300000;  // 30mSec early
  1397.     m_trThrottle = 0;
  1398.     m_trRememberStampForPerf = 0;
  1399. #ifdef PERF
  1400.     m_trRememberFrameForPerf = 0;
  1401. #endif
  1402.     return NOERROR;
  1403. } // ResetStreamingTimes
  1404. // Reset all times controlling streaming. Note that we're now streaming. We
  1405. // don't need to set the rendering event to have the source filter released
  1406. // as it is done during the Run processing. When we are run we immediately
  1407. // release the source filter thread and draw any image waiting (that image
  1408. // may already have been drawn once as a poster frame while we were paused)
  1409. HRESULT CBaseVideoRenderer::OnStartStreaming()
  1410. {
  1411.     ResetStreamingTimes();
  1412.     return NOERROR;
  1413. } // OnStartStreaming
  1414. // Called at end of streaming.  Fixes times for property page report
  1415. HRESULT CBaseVideoRenderer::OnStopStreaming()
  1416. {
  1417.     m_tStreamingStart = timeGetTime()-m_tStreamingStart;
  1418.     return NOERROR;
  1419. } // OnStopStreaming
  1420. // Called when we start waiting for a rendering event.
  1421. // Used to update times spent waiting and not waiting.
  1422. void CBaseVideoRenderer::OnWaitStart()
  1423. {
  1424.     MSR_START(m_idWaitReal);
  1425. } // OnWaitStart
  1426. // Called when we are awoken from the wait in the window OR by our allocator
  1427. // when it is hanging around until the next sample is due for rendering on a
  1428. // DCI/DirectDraw surface. We add the wait time into our rolling average.
  1429. // We grab the interface lock so that we're serialised with the application
  1430. // thread going through the run code - which in due course ends up calling
  1431. // ResetStreaming times - possibly as we run through this section of code
  1432. void CBaseVideoRenderer::OnWaitEnd()
  1433. {
  1434. #ifdef PERF
  1435.     MSR_STOP(m_idWaitReal);
  1436.     // for a perf build we want to know just exactly how late we REALLY are.
  1437.     // even if this means that we have to look at the clock again.
  1438.     REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.
  1439. #if 0
  1440.     m_pClock->GetTime(&trRealStream); // Calling clock here causes W95 deadlock!
  1441. #else
  1442.     // We will be discarding overflows like mad here!
  1443.     // This is wrong really because timeGetTime() can wrap but it's
  1444.     // only for PERF
  1445.     REFERENCE_TIME tr = timeGetTime()*10000;
  1446.     trRealStream = tr + m_llTimeOffset;
  1447. #endif
  1448.     trRealStream -= m_tStart;     // convert to stream time (this is a reftime)
  1449.     if (m_trRememberStampForPerf==0) {
  1450.         // This is probably the poster frame at the start, and it is not scheduled
  1451.         // in the usual way at all.  Just count it.  The rememberstamp gets set
  1452.         // in ShouldDrawSampleNow, so this does invalid frame recording until we
  1453.         // actually start playing.
  1454.         PreparePerformanceData(0, 0);
  1455.     } else {
  1456.         int trLate = (int)(trRealStream - m_trRememberStampForPerf);
  1457.         int trFrame = (int)(tr - m_trRememberFrameForPerf);
  1458.         PreparePerformanceData(trLate, trFrame);
  1459.     }
  1460.     m_trRememberFrameForPerf = tr;
  1461. #endif //PERF
  1462. } // OnWaitEnd
  1463. // Put data on one side that describes the lateness of the current frame.
  1464. // We don't yet know whether it will actually be drawn.  In direct draw mode,
  1465. // this decision is up to the filter upstream, and it could change its mind.
  1466. // The rules say that if it did draw it must call Receive().  One way or
  1467. // another we eventually get into either OnRenderStart or OnDirectRender and
  1468. // these both call RecordFrameLateness to update the statistics.
  1469. void CBaseVideoRenderer::PreparePerformanceData(int trLate, int trFrame)
  1470. {
  1471.     m_trLate = trLate;
  1472.     m_trFrame = trFrame;
  1473. } // PreparePerformanceData
  1474. // update the statistics:
  1475. // m_iTotAcc, m_iSumSqAcc, m_iSumSqFrameTime, m_iSumFrameTime, m_cFramesDrawn
  1476. // Note that because the properties page reports using these variables,
  1477. // 1. We need to be inside a critical section
  1478. // 2. They must all be updated together.  Updating the sums here and the count
  1479. // elsewhere can result in imaginary jitter (i.e. attempts to find square roots
  1480. // of negative numbers) in the property page code.
  1481. void CBaseVideoRenderer::RecordFrameLateness(int trLate, int trFrame)
  1482. {
  1483.     // Record how timely we are.
  1484.     int tLate = trLate/10000;
  1485.     // Best estimate of moment of appearing on the screen is average of
  1486.     // start and end draw times.  Here we have only the end time.  This may
  1487.     // tend to show us as spuriously late by up to 1/2 frame rate achieved.
  1488.     // Decoder probably monitors draw time.  We don't bother.
  1489.     MSR_INTEGER( m_idFrameAccuracy, tLate );
  1490.     // This is a kludge - we can get frames that are very late
  1491.     // especially (at start-up) and they invalidate the statistics.
  1492.     // So ignore things that are more than 1 sec off.
  1493.     if (tLate>1000 || tLate<-1000) {
  1494.         if (m_cFramesDrawn<=1) {
  1495.             tLate = 0;
  1496.         } else if (tLate>0) {
  1497.             tLate = 1000;
  1498.         } else {
  1499.             tLate = -1000;
  1500.         }
  1501.     }
  1502.     // The very first frame often has a invalid time, so don't
  1503.     // count it into the statistics.   (???)
  1504.     if (m_cFramesDrawn>1) {
  1505.         m_iTotAcc += tLate;
  1506.         m_iSumSqAcc += (tLate*tLate);
  1507.     }
  1508.     // calculate inter-frame time.  Doesn't make sense for first frame
  1509.     // second frame suffers from invalid first frame stamp.
  1510.     if (m_cFramesDrawn>2) {
  1511.         int tFrame = trFrame/10000;    // convert to mSec else it overflows
  1512.         // This is a kludge.  It can overflow anyway (a pause can cause
  1513.         // a very long inter-frame time) and it overflows at 2**31/10**7
  1514.         // or about 215 seconds i.e. 3min 35sec
  1515.         if (tFrame>1000||tFrame<0) tFrame = 1000;
  1516.         m_iSumSqFrameTime += tFrame*tFrame;
  1517.         ASSERT(m_iSumSqFrameTime>=0);
  1518.         m_iSumFrameTime += tFrame;
  1519.     }
  1520.     ++m_cFramesDrawn;
  1521. } // RecordFrameLateness
  1522. void CBaseVideoRenderer::ThrottleWait()
  1523. {
  1524.     if (m_trThrottle>0) {
  1525.         int iThrottle = m_trThrottle/10000;    // convert to mSec
  1526.         MSR_INTEGER( m_idThrottle, iThrottle);
  1527.         DbgLog((LOG_TRACE, 0, TEXT("Throttle %d ms"), iThrottle));
  1528.         Sleep(iThrottle);
  1529.     } else {
  1530.         Sleep(0);
  1531.     }
  1532. } // ThrottleWait
  1533. // Whenever a frame is rendered it goes though either OnRenderStart
  1534. // or OnDirectRender.  Data that are generated during ShouldDrawSample
  1535. // are added to the statistics by calling RecordFrameLateness from both
  1536. // these two places.
  1537. // Called in place of OnRenderStart..OnRenderEnd
  1538. // When a DirectDraw image is drawn
  1539. void CBaseVideoRenderer::OnDirectRender(IMediaSample *pMediaSample)
  1540. {
  1541.     m_trRenderAvg = 0;
  1542.     m_trRenderLast = 5000000;  // If we mode switch, we do NOT want this
  1543.                                // to inhibit the new average getting going!
  1544.                                // so we set it to half a second
  1545.     // MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
  1546.     RecordFrameLateness(m_trLate, m_trFrame);
  1547.     ThrottleWait();
  1548. } // OnDirectRender
  1549. // Called just before we start drawing.  All we do is to get the current clock
  1550. // time (from the system) and return.  We have to store the start render time
  1551. // in a member variable because it isn't used until we complete the drawing
  1552. // The rest is just performance logging.
  1553. void CBaseVideoRenderer::OnRenderStart(IMediaSample *pMediaSample)
  1554. {
  1555.     RecordFrameLateness(m_trLate, m_trFrame);
  1556.     m_tRenderStart = timeGetTime();
  1557. } // OnRenderStart
  1558. // Called directly after drawing an image.  We calculate the time spent in the
  1559. // drawing code and if this doesn't appear to have any odd looking spikes in
  1560. // it then we add it to the current average draw time.  Measurement spikes may
  1561. // occur if the drawing thread is interrupted and switched to somewhere else.
  1562. void CBaseVideoRenderer::OnRenderEnd(IMediaSample *pMediaSample)
  1563. {
  1564.     // The renderer time can vary erratically if we are interrupted so we do
  1565.     // some smoothing to help get more sensible figures out but even that is
  1566.     // not enough as figures can go 9,10,9,9,83,9 and we must disregard 83
  1567.     int tr = (timeGetTime() - m_tRenderStart)*10000;   // convert mSec->UNITS
  1568.     if (tr < m_trRenderAvg*2 || tr < 2 * m_trRenderLast) {
  1569.         // DO_MOVING_AVG(m_trRenderAvg, tr);
  1570.         m_trRenderAvg = (tr + (AVGPERIOD-1)*m_trRenderAvg)/AVGPERIOD;
  1571.     }
  1572.     m_trRenderLast = tr;
  1573.     ThrottleWait();
  1574. } // OnRenderEnd
  1575. STDMETHODIMP CBaseVideoRenderer::SetSink( IQualityControl * piqc)
  1576. {
  1577.     m_pQSink = piqc;
  1578.     return NOERROR;
  1579. } // SetSink
  1580. STDMETHODIMP CBaseVideoRenderer::Notify( IBaseFilter * pSelf, Quality q)
  1581. {
  1582.     // NOTE:  We are NOT getting any locks here.  We could be called
  1583.     // asynchronously and possibly even on a time critical thread of
  1584.     // someone else's - so we do the minumum.  We only set one state
  1585.     // variable (an integer) and if that happens to be in the middle
  1586.     // of another thread reading it they will just get either the new
  1587.     // or the old value.  Locking would achieve no more than this.
  1588.     // It might be nice to check that we are being called from m_pGraph, but
  1589.     // it turns out to be a millisecond or so per throw!
  1590.     // This is heuristics, these numbers are aimed at being "what works"
  1591.     // rather than anything based on some theory.
  1592.     // We use a hyperbola because it's easy to calculate and it includes
  1593.     // a panic button asymptote (which we push off just to the left)
  1594.     // The throttling fits the following table (roughly)
  1595.     // Proportion   Throttle (msec)
  1596.     //     >=1000         0
  1597.     //        900         3
  1598.     //        800         7
  1599.     //        700        11
  1600.     //        600        17
  1601.     //        500        25
  1602.     //        400        35
  1603.     //        300        50
  1604.     //        200        72
  1605.     //        125       100
  1606.     //        100       112
  1607.     //         50       146
  1608.     //          0       200
  1609.     // (some evidence that we could go for a sharper kink - e.g. no throttling
  1610.     // until below the 750 mark - might give fractionally more frames on a
  1611.     // P60-ish machine).  The easy way to get these coefficients is to use
  1612.     // Renbase.xls follow the instructions therein using excel solver.
  1613.     if (q.Proportion>=1000) { m_trThrottle = 0; }
  1614.     else {
  1615.         // The DWORD is to make quite sure I get unsigned arithmetic
  1616.         // as the constant is between 2**31 and 2**32
  1617.         m_trThrottle = -330000 + (388880000/(q.Proportion+167));
  1618.     }
  1619.     return NOERROR;
  1620. } // Notify
  1621. // Send a message to indicate what our supplier should do about quality.
  1622. // Theory:
  1623. // What a supplier wants to know is "is the frame I'm working on NOW
  1624. // going to be late?".
  1625. // F1 is the frame at the supplier (as above)
  1626. // Tf1 is the due time for F1
  1627. // T1 is the time at that point (NOW!)
  1628. // Tr1 is the time that f1 WILL actually be rendered
  1629. // L1 is the latency of the graph for frame F1 = Tr1-T1
  1630. // D1 (for delay) is how late F1 will be beyond its due time i.e.
  1631. // D1 = (Tr1-Tf1) which is what the supplier really wants to know.
  1632. // Unfortunately Tr1 is in the future and is unknown, so is L1
  1633. //
  1634. // We could estimate L1 by its value for a previous frame,
  1635. // L0 = Tr0-T0 and work off
  1636. // D1' = ((T1+L0)-Tf1) = (T1 + (Tr0-T0) -Tf1)
  1637. // Rearranging terms:
  1638. // D1' = (T1-T0) + (Tr0-Tf1)
  1639. //       adding (Tf0-Tf0) and rearranging again:
  1640. //     = (T1-T0) + (Tr0-Tf0) + (Tf0-Tf1)
  1641. //     = (T1-T0) - (Tf1-Tf0) + (Tr0-Tf0)
  1642. // But (Tr0-Tf0) is just D0 - how late frame zero was, and this is the
  1643. // Late field in the quality message that we send.
  1644. // The other two terms just state what correction should be applied before
  1645. // using the lateness of F0 to predict the lateness of F1.
  1646. // (T1-T0) says how much time has actually passed (we have lost this much)
  1647. // (Tf1-Tf0) says how much time should have passed if we were keeping pace
  1648. // (we have gained this much).
  1649. //
  1650. // Suppliers should therefore work off:
  1651. //    Quality.Late + (T1-T0)  - (Tf1-Tf0)
  1652. // and see if this is "acceptably late" or even early (i.e. negative).
  1653. // They get T1 and T0 by polling the clock, they get Tf1 and Tf0 from
  1654. // the time stamps in the frames.  They get Quality.Late from us.
  1655. //
  1656. HRESULT CBaseVideoRenderer::SendQuality(REFERENCE_TIME trLate,
  1657.                                         REFERENCE_TIME trRealStream)
  1658. {
  1659.     Quality q;
  1660.     HRESULT hr;
  1661.     // If we are the main user of time, then report this as Flood/Dry.
  1662.     // If our suppliers are, then report it as Famine/Glut.
  1663.     //
  1664.     // We need to take action, but avoid hunting.  Hunting is caused by
  1665.     // 1. Taking too much action too soon and overshooting
  1666.     // 2. Taking too long to react (so averaging can CAUSE hunting).
  1667.     //
  1668.     // The reason why we use trLate as well as Wait is to reduce hunting;
  1669.     // if the wait time is coming down and about to go into the red, we do
  1670.     // NOT want to rely on some average which is only telling is that it used
  1671.     // to be OK once.
  1672.     q.TimeStamp = (REFERENCE_TIME)trRealStream;
  1673.     if (m_trFrameAvg<0) {
  1674.         q.Type = Famine;      // guess
  1675.     }
  1676.     // Is the greater part of the time taken bltting or something else
  1677.     else if (m_trFrameAvg > 2*m_trRenderAvg) {
  1678.         q.Type = Famine;                        // mainly other
  1679.     } else {
  1680.         q.Type = Flood;                         // mainly bltting
  1681.     }
  1682.     q.Proportion = 1000;               // default
  1683.     if (m_trFrameAvg<0) {
  1684.         // leave it alone - we don't know enough
  1685.     }
  1686.     else if ( trLate> 0 ) {
  1687.         // try to catch up over the next second
  1688.         // We could be Really, REALLY late, but rendering all the frames
  1689.         // anyway, just because it's so cheap.
  1690.         q.Proportion = 1000 - (int)((trLate)/(UNITS/1000));
  1691.         if (q.Proportion<500) {
  1692.            q.Proportion = 500;      // don't go daft. (could've been negative!)
  1693.         } else {
  1694.         }
  1695.     } else if (  m_trWaitAvg>20000
  1696.               && trLate<-20000
  1697.               ){
  1698.         // Go cautiously faster - aim at 2mSec wait.
  1699.         if (m_trWaitAvg>=m_trFrameAvg) {
  1700.             // This can happen because of some fudges.
  1701.             // The waitAvg is how long we originally planned to wait
  1702.             // The frameAvg is more honest.
  1703.             // It means that we are spending a LOT of time waiting
  1704.             q.Proportion = 2000;    // double.
  1705.         } else {
  1706.             if (m_trFrameAvg+20000 > m_trWaitAvg) {
  1707.                 q.Proportion
  1708.                     = 1000 * (m_trFrameAvg / (m_trFrameAvg + 20000 - m_trWaitAvg));
  1709.             } else {
  1710.                 // We're apparently spending more than the whole frame time waiting.
  1711.                 // Assume that the averages are slightly out of kilter, but that we
  1712.                 // are indeed doing a lot of waiting.  (This leg probably never
  1713.                 // happens, but the code avoids any potential divide by zero).
  1714.                 q.Proportion = 2000;
  1715.             }
  1716.         }
  1717.         if (q.Proportion>2000) {
  1718.             q.Proportion = 2000;    // don't go crazy.
  1719.         }
  1720.     }
  1721.     // Tell the supplier how late frames are when they get rendered
  1722.     // That's how late we are now.
  1723.     // If we are in directdraw mode then the guy upstream can see the drawing
  1724.     // times and we'll just report on the start time.  He can figure out any
  1725.     // offset to apply.  If we are in DIB Section mode then we will apply an
  1726.     // extra offset which is half of our drawing time.  This is usually small
  1727.     // but can sometimes be the dominant effect.  For this we will use the
  1728.     // average drawing time rather than the last frame.  If the last frame took
  1729.     // a long time to draw and made us late, that's already in the lateness
  1730.     // figure.  We should not add it in again unless we expect the next frame
  1731.     // to be the same.  We don't, we expect the average to be a better shot.
  1732.     // In direct draw mode the RenderAvg will be zero.
  1733.     q.Late = trLate + m_trRenderAvg/2;
  1734.     // log what we're doing
  1735.     MSR_INTEGER(m_idQualityRate, q.Proportion);
  1736.     MSR_INTEGER( m_idQualityTime, (int)q.Late / 10000 );
  1737.     // A specific sink interface may be set through IPin
  1738.     if (m_pQSink==NULL) {
  1739.         // Get our input pin's peer.  We send quality management messages
  1740.         // to any nominated receiver of these things (set in the IPin
  1741.         // interface), or else to our source filter.
  1742.         IQualityControl *pQC = NULL;
  1743.         IPin *pOutputPin = m_pInputPin->GetConnected();
  1744.         ASSERT(pOutputPin != NULL);
  1745.         // And get an AddRef'd quality control interface
  1746.         hr = pOutputPin->QueryInterface(IID_IQualityControl,(void**) &pQC);
  1747.         if (SUCCEEDED(hr)) {
  1748.             m_pQSink = pQC;
  1749.         }
  1750.     }
  1751.     if (m_pQSink) {
  1752.         return m_pQSink->Notify(this,q);
  1753.     }
  1754.     return S_FALSE;
  1755. } // SendQuality
  1756. // We are called with a valid IMediaSample image to decide whether this is to
  1757. // be drawn or not.  There must be a reference clock in operation.
  1758. // Return S_OK if it is to be drawn Now (as soon as possible)
  1759. // Return S_FALSE if it is to be drawn when it's due
  1760. // Return an error if we want to drop it
  1761. // m_nNormal=-1 indicates that we dropped the previous frame and so this
  1762. // one should be drawn early.  Respect it and update it.
  1763. // Use current stream time plus a number of heuristics (detailed below)
  1764. // to make the decision
  1765. HRESULT CBaseVideoRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,
  1766.                                                 REFERENCE_TIME *ptrStart,
  1767.                                                 REFERENCE_TIME *ptrEnd)
  1768. {
  1769.     // Don't call us unless there's a clock interface to synchronise with
  1770.     ASSERT(m_pClock);
  1771.     MSR_INTEGER(m_idTimeStamp, (int)((*ptrStart)>>32));   // high order 32 bits
  1772.     MSR_INTEGER(m_idTimeStamp, (int)(*ptrStart));         // low order 32 bits
  1773.     // We lose a bit of time depending on the monitor type waiting for the next
  1774.     // screen refresh.  On average this might be about 8mSec - so it will be
  1775.     // later than we think when the picture appears.  To compensate a bit
  1776.     // we bias the media samples by -8mSec i.e. 80000 UNITs.
  1777.     // We don't ever make a stream time negative (call it paranoia)
  1778.     if (*ptrStart>=80000) {
  1779.         *ptrStart -= 80000;
  1780.         *ptrEnd -= 80000;       // bias stop to to retain valid frame duration
  1781.     }
  1782.     // Cache the time stamp now.  We will want to compare what we did with what
  1783.     // we started with (after making the monitor allowance).
  1784.     m_trRememberStampForPerf = *ptrStart;
  1785.     // Get reference times (current and late)
  1786.     REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.
  1787.     m_pClock->GetTime(&trRealStream);
  1788. #ifdef PERF
  1789.     // While the reference clock is expensive:
  1790.     // Remember the offset from timeGetTime and use that.
  1791.     // This overflows all over the place, but when we subtract to get
  1792.     // differences the overflows all cancel out.
  1793.     m_llTimeOffset = trRealStream-timeGetTime()*10000;
  1794. #endif
  1795.     trRealStream -= m_tStart;     // convert to stream time (this is a reftime)
  1796.     // We have to wory about two versions of "lateness".  The truth, which we
  1797.     // try to work out here and the one measured against m_trTarget which
  1798.     // includes long term feedback.  We report statistics against the truth
  1799.     // but for operational decisions we work to the target.
  1800.     // We use TimeDiff to make sure we get an integer because we
  1801.     // may actually be late (or more likely early if there is a big time
  1802.     // gap) by a very long time.
  1803.     const int trTrueLate = TimeDiff(trRealStream - *ptrStart);
  1804.     const int trLate = trTrueLate;
  1805.     MSR_INTEGER(m_idSchLateTime, trTrueLate/10000);
  1806.     // Send quality control messages upstream, measured against target
  1807.     HRESULT hr = SendQuality(trLate, trRealStream);
  1808.     // Note: the filter upstream is allowed to this FAIL meaning "you do it".
  1809.     m_bSupplierHandlingQuality = (hr==S_OK);
  1810.     // Decision time!  Do we drop, draw when ready or draw immediately?
  1811.     const int trDuration = (int)(*ptrEnd - *ptrStart);
  1812.     {
  1813.         // We need to see if the frame rate of the file has just changed.
  1814.         // This would make comparing our previous frame rate with the current
  1815.         // frame rate inefficent.  Hang on a moment though.  I've seen files
  1816.         // where the frames vary between 33 and 34 mSec so as to average
  1817.         // 30fps.  A minor variation like that won't hurt us.
  1818.         int t = m_trDuration/32;
  1819.         if (  trDuration > m_trDuration+t
  1820.            || trDuration < m_trDuration-t
  1821.            ) {
  1822.             // There's a major variation.  Reset the average frame rate to
  1823.             // exactly the current rate to disable decision 9002 for this frame,
  1824.             // and remember the new rate.
  1825.             m_trFrameAvg = trDuration;
  1826.             m_trDuration = trDuration;
  1827.         }
  1828.     }
  1829.     MSR_INTEGER(m_idEarliness, m_trEarliness/10000);
  1830.     MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);
  1831.     MSR_INTEGER(m_idFrameAvg, m_trFrameAvg/10000);
  1832.     MSR_INTEGER(m_idWaitAvg, m_trWaitAvg/10000);
  1833.     MSR_INTEGER(m_idDuration, trDuration/10000);
  1834. #ifdef PERF
  1835.     if (S_OK==pMediaSample->IsDiscontinuity()) {
  1836.         MSR_INTEGER(m_idDecision, 9000);
  1837.     }
  1838. #endif
  1839.     // Control the graceful slide back from slow to fast machine mode.
  1840.     // After a frame drop accept an early frame and set the earliness to here
  1841.     // If this frame is already later than the earliness then slide it to here
  1842.     // otherwise do the standard slide (reduce by about 12% per frame).
  1843.     // Note: earliness is normally NEGATIVE
  1844.     BOOL bJustDroppedFrame
  1845.         = (  m_bSupplierHandlingQuality
  1846.           //  Can't use the pin sample properties because we might
  1847.           //  not be in Receive when we call this
  1848.           && (S_OK == pMediaSample->IsDiscontinuity())          // he just dropped one
  1849.           )
  1850.        || (m_nNormal==-1);                          // we just dropped one
  1851.     // Set m_trEarliness (slide back from slow to fast machine mode)
  1852.     if (trLate>0) {
  1853.         m_trEarliness = 0;   // we are no longer in fast machine mode at all!
  1854.     } else if (  (trLate>=m_trEarliness) || bJustDroppedFrame) {
  1855.         m_trEarliness = trLate;  // Things have slipped of their own accord
  1856.     } else {
  1857.         m_trEarliness = m_trEarliness - m_trEarliness/8;  // graceful slide
  1858.     }
  1859.     // prepare the new wait average - but don't pollute the old one until
  1860.     // we have finished with it.
  1861.     int trWaitAvg;
  1862.     {
  1863.         // We never mix in a negative wait.  This causes us to believe in fast machines
  1864.         // slightly more.
  1865.         int trL = trLate<0 ? -trLate : 0;
  1866.         trWaitAvg = (trL + m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
  1867.     }
  1868.     int trFrame;
  1869.     {
  1870.         REFERENCE_TIME tr = trRealStream - m_trLastDraw; // Cd be large - 4 min pause!
  1871.         if (tr>10000000) {
  1872.             tr = 10000000;   // 1 second - arbitrarily.
  1873.         }
  1874.         trFrame = int(tr);
  1875.     }
  1876.     // We will DRAW this frame IF...
  1877.     if (
  1878.           // ...the time we are spending drawing is a small fraction of the total
  1879.           // observed inter-frame time so that dropping it won't help much.
  1880.           (3*m_trRenderAvg <= m_trFrameAvg)
  1881.          // ...or our supplier is NOT handling things and the next frame would
  1882.          // be less timely than this one or our supplier CLAIMS to be handling
  1883.          // things, and is now less than a full FOUR frames late.
  1884.        || ( m_bSupplierHandlingQuality
  1885.           ? (trLate <= trDuration*4)
  1886.           : (trLate+trLate < trDuration)
  1887.           )
  1888.           // ...or we are on average waiting for over eight milliseconds then
  1889.           // this may be just a glitch.  Draw it and we'll hope to catch up.
  1890.        || (m_trWaitAvg > 80000)
  1891.           // ...or we haven't drawn an image for over a second.  We will update
  1892.           // the display, which stops the video looking hung.
  1893.           // Do this regardless of how late this media sample is.
  1894.        || ((trRealStream - m_trLastDraw) > UNITS)
  1895.     ) {
  1896.         HRESULT Result;
  1897.         // We are going to play this frame.  We may want to play it early.
  1898.         // We will play it early if we think we are in slow machine mode.
  1899.         // If we think we are NOT in slow machine mode, we will still play
  1900.         // it early by m_trEarliness as this controls the graceful slide back.
  1901.         // and in addition we aim at being m_trTarget late rather than "on time".
  1902.         BOOL bPlayASAP = FALSE;
  1903.         // we will play it AT ONCE (slow machine mode) if...
  1904.             // ...we are playing catch-up
  1905.         if ( bJustDroppedFrame) {
  1906.             bPlayASAP = TRUE;
  1907.             MSR_INTEGER(m_idDecision, 9001);
  1908.         }
  1909.             // ...or if we are running below the true frame rate
  1910.             // exact comparisons are glitchy, for these measurements,
  1911.             // so add an extra 5% or so
  1912.         else if (  (m_trFrameAvg > trDuration + trDuration/16)
  1913.                    // It's possible to get into a state where we are losing ground, but
  1914.                    // are a very long way ahead.  To avoid this or recover from it
  1915.                    // we refuse to play early by more than 10 frames.
  1916.                 && (trLate > - trDuration*10)
  1917.                 ){
  1918.             bPlayASAP = TRUE;
  1919.             MSR_INTEGER(m_idDecision, 9002);
  1920.         }
  1921. #if 0
  1922.             // ...or if we have been late and are less than one frame early
  1923.         else if (  (trLate + trDuration > 0)
  1924.                 && (m_trWaitAvg<=20000)
  1925.                 ) {
  1926.             bPlayASAP = TRUE;
  1927.             MSR_INTEGER(m_idDecision, 9003);
  1928.         }
  1929. #endif
  1930.         // We will NOT play it at once if we are grossly early.  On very slow frame
  1931.         // rate movies - e.g. clock.avi - it is not a good idea to leap ahead just
  1932.         // because we got starved (for instance by the net) and dropped one frame
  1933.         // some time or other.  If we are more than 900mSec early, then wait.
  1934.         if (trLate<-9000000) {
  1935.             bPlayASAP = FALSE;
  1936.         }
  1937.         if (bPlayASAP) {
  1938.             m_nNormal = 0;
  1939.             MSR_INTEGER(m_idDecision, 0);
  1940.             // When we are here, we are in slow-machine mode.  trLate may well
  1941.             // oscillate between negative and positive when the supplier is
  1942.             // dropping frames to keep sync.  We should not let that mislead
  1943.             // us into thinking that we have as much as zero spare time!
  1944.             // We just update with a zero wait.
  1945.             m_trWaitAvg = (m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;
  1946.             // Assume that we draw it immediately.  Update inter-frame stats
  1947.             m_trFrameAvg = (trFrame + m_trFrameAvg*(AVGPERIOD-1))/AVGPERIOD;
  1948. #ifndef PERF
  1949.             // If this is NOT a perf build, then report what we know so far
  1950.             // without looking at the clock any more.  This assumes that we
  1951.             // actually wait for exactly the time we hope to.  It also reports
  1952.             // how close we get to the manipulated time stamps that we now have
  1953.             // rather than the ones we originally started with.  It will
  1954.             // therefore be a little optimistic.  However it's fast.
  1955.             PreparePerformanceData(trTrueLate, trFrame);
  1956. #endif
  1957.             m_trLastDraw = trRealStream;
  1958.             if (m_trEarliness > trLate) {
  1959.                 m_trEarliness = trLate;  // if we are actually early, this is neg
  1960.             }
  1961.             Result = S_OK;                   // Draw it now
  1962.         } else {
  1963.             ++m_nNormal;
  1964.             // Set the average frame rate to EXACTLY the ideal rate.
  1965.             // If we are exiting slow-machine mode then we will have caught up
  1966.             // and be running ahead, so as we slide back to exact timing we will
  1967.             // have a longer than usual gap at this point.  If we record this
  1968.             // real gap then we'll think that we're running slow and go back
  1969.             // into slow-machine mode and vever get it straight.
  1970.             m_trFrameAvg = trDuration;
  1971.             MSR_INTEGER(m_idDecision, 1);
  1972.             // Play it early by m_trEarliness and by m_trTarget
  1973.             {
  1974.                 int trE = m_trEarliness;
  1975.                 if (trE < -m_trFrameAvg) {
  1976.                     trE = -m_trFrameAvg;
  1977.                 }
  1978.                 *ptrStart += trE;           // N.B. earliness is negative
  1979.             }
  1980.             int Delay = -trTrueLate;
  1981.             Result = Delay<=0 ? S_OK : S_FALSE;     // OK = draw now, FALSE = wait
  1982.             m_trWaitAvg = trWaitAvg;
  1983.             // Predict when it will actually be drawn and update frame stats
  1984.             if (Result==S_FALSE) {   // We are going to wait
  1985.                 trFrame = TimeDiff(*ptrStart-m_trLastDraw);
  1986.                 m_trLastDraw = *ptrStart;
  1987.             } else {
  1988.                 // trFrame is already = trRealStream-m_trLastDraw;
  1989.                 m_trLastDraw = trRealStream;
  1990.             }
  1991. #ifndef PERF
  1992.             int iAccuracy;
  1993.             if (Delay>0) {
  1994.                 // Report lateness based on when we intend to play it
  1995.                 iAccuracy = TimeDiff(*ptrStart-m_trRememberStampForPerf);
  1996.             } else {
  1997.                 // Report lateness based on playing it *now*.
  1998.                 iAccuracy = trTrueLate;     // trRealStream-RememberStampForPerf;
  1999.             }
  2000.             PreparePerformanceData(iAccuracy, trFrame);
  2001. #endif
  2002.         }
  2003.         return Result;
  2004.     }
  2005.     // We are going to drop this frame!
  2006.     // Of course in DirectDraw mode the guy upstream may draw it anyway.
  2007.     // This will probably give a large negative wack to the wait avg.
  2008.     m_trWaitAvg = trWaitAvg;
  2009. #ifdef PERF
  2010.     // Respect registry setting - debug only!
  2011.     if (m_bDrawLateFrames) {
  2012.        return S_OK;                        // draw it when it's ready
  2013.     }                                      // even though it's late.
  2014. #endif
  2015.     // We are going to drop this frame so draw the next one early
  2016.     // n.b. if the supplier is doing direct draw then he may draw it anyway
  2017.     // but he's doing something funny to arrive here in that case.
  2018.     MSR_INTEGER(m_idDecision, 2);
  2019.     m_nNormal = -1;
  2020.     return E_FAIL;                         // drop it
  2021. } // ShouldDrawSampleNow
  2022. // NOTE we're called by both the window thread and the source filter thread
  2023. // so we have to be protected by a critical section (locked before called)
  2024. // Also, when the window thread gets signalled to render an image, it always
  2025. // does so regardless of how late it is. All the degradation is done when we
  2026. // are scheduling the next sample to be drawn. Hence when we start an advise
  2027. // link to draw a sample, that sample's time will always become the last one
  2028. // drawn - unless of course we stop streaming in which case we cancel links
  2029. BOOL CBaseVideoRenderer::ScheduleSample(IMediaSample *pMediaSample)
  2030. {
  2031.     // We override ShouldDrawSampleNow to add quality management
  2032.     BOOL bDrawImage = CBaseRenderer::ScheduleSample(pMediaSample);
  2033.     if (bDrawImage == FALSE) {
  2034. ++m_cFramesDropped;
  2035. return FALSE;
  2036.     }
  2037.     // m_cFramesDrawn must NOT be updated here.  It has to be updated
  2038.     // in RecordFrameLateness at the same time as the other statistics.
  2039.     return TRUE;
  2040. }
  2041. // Implementation of IQualProp interface needed to support the property page
  2042. // This is how the property page gets the data out of the scheduler. We are
  2043. // passed into the constructor the owning object in the COM sense, this will
  2044. // either be the video renderer or an external IUnknown if we're aggregated.
  2045. // We initialise our CUnknown base class with this interface pointer. Then
  2046. // all we have to do is to override NonDelegatingQueryInterface to expose
  2047. // our IQualProp interface. The AddRef and Release are handled automatically
  2048. // by the base class and will be passed on to the appropriate outer object
  2049. STDMETHODIMP CBaseVideoRenderer::get_FramesDroppedInRenderer(int *pcFramesDropped)
  2050. {
  2051.     CheckPointer(pcFramesDropped,E_POINTER);
  2052.     CAutoLock cVideoLock(&m_InterfaceLock);
  2053.     *pcFramesDropped = m_cFramesDropped;
  2054.     return NOERROR;
  2055. } // get_FramesDroppedInRenderer
  2056. // Set *pcFramesDrawn to the number of frames drawn since
  2057. // streaming started.
  2058. STDMETHODIMP CBaseVideoRenderer::get_FramesDrawn( int *pcFramesDrawn)
  2059. {
  2060.     CheckPointer(pcFramesDrawn,E_POINTER);
  2061.     CAutoLock cVideoLock(&m_InterfaceLock);
  2062.     *pcFramesDrawn = m_cFramesDrawn;
  2063.     return NOERROR;
  2064. } // get_FramesDrawn
  2065. // Set iAvgFrameRate to the frames per hundred secs since
  2066. // streaming started.  0 otherwise.
  2067. STDMETHODIMP CBaseVideoRenderer::get_AvgFrameRate( int *piAvgFrameRate)
  2068. {
  2069.     CheckPointer(piAvgFrameRate,E_POINTER);
  2070.     CAutoLock cVideoLock(&m_InterfaceLock);
  2071.     int t;
  2072.     if (m_bStreaming) {
  2073.         t = timeGetTime()-m_tStreamingStart;
  2074.     } else {
  2075.         t = m_tStreamingStart;
  2076.     }
  2077.     if (t<=0) {
  2078.         *piAvgFrameRate = 0;
  2079.         ASSERT(m_cFramesDrawn == 0);
  2080.     } else {
  2081.         // i is frames per hundred seconds
  2082.         *piAvgFrameRate = MulDiv(100000, m_cFramesDrawn, t);
  2083.     }
  2084.     return NOERROR;
  2085. } // get_AvgFrameRate
  2086. // Set *piAvg to the average sync offset since streaming started
  2087. // in mSec.  The sync offset is the time in mSec between when the frame
  2088. // should have been drawn and when the frame was actually drawn.
  2089. STDMETHODIMP CBaseVideoRenderer::get_AvgSyncOffset( int *piAvg)
  2090. {
  2091.     CheckPointer(piAvg,E_POINTER);
  2092.     CAutoLock cVideoLock(&m_InterfaceLock);
  2093.     if (NULL==m_pClock) {
  2094.         *piAvg = 0;
  2095.         return NOERROR;
  2096.     }
  2097.     // Note that we didn't gather the stats on the first frame
  2098.     // so we use m_cFramesDrawn-1 here
  2099.     if (m_cFramesDrawn<=1) {
  2100.         *piAvg = 0;
  2101.     } else {
  2102.         *piAvg = (int)(m_iTotAcc / (m_cFramesDrawn-1));
  2103.     }
  2104.     return NOERROR;
  2105. } // get_AvgSyncOffset
  2106. // To avoid dragging in the maths library - a cheap
  2107. // approximate integer square root.
  2108. // We do this by getting a starting guess which is between 1
  2109. // and 2 times too large, followed by THREE iterations of
  2110. // Newton Raphson.  (That will give accuracy to the nearest mSec
  2111. // for the range in question - roughly 0..1000)
  2112. //
  2113. // It would be faster to use a linear interpolation and ONE NR, but
  2114. // who cares.  If anyone does - the best linear interpolation is
  2115. // to approximates sqrt(x) by
  2116. // y = x * (sqrt(2)-1) + 1 - 1/sqrt(2) + 1/(8*(sqrt(2)-1))
  2117. // 0r y = x*0.41421 + 0.59467
  2118. // This minimises the maximal error in the range in question.
  2119. // (error is about +0.008883 and then one NR will give error .0000something
  2120. // (Of course these are integers, so you can't just multiply by 0.41421
  2121. // you'd have to do some sort of MulDiv).
  2122. // Anyone wanna check my maths?  (This is only for a property display!)
  2123. int isqrt(int x)
  2124. {
  2125.     int s = 1;
  2126.     // Make s an initial guess for sqrt(x)
  2127.     if (x > 0x40000000) {
  2128.        s = 0x8000;     // prevent any conceivable closed loop
  2129.     } else {
  2130.         while (s*s<x) {    // loop cannot possible go more than 31 times
  2131.             s = 2*s;       // normally it goes about 6 times
  2132.         }
  2133.         // Three NR iterations.
  2134.         if (x==0) {
  2135.            s= 0; // Wouldn't it be tragic to divide by zero whenever our
  2136.                  // accuracy was perfect!
  2137.         } else {
  2138.             s = (s*s+x)/(2*s);
  2139.             if (s>=0) s = (s*s+x)/(2*s);
  2140.             if (s>=0) s = (s*s+x)/(2*s);
  2141.         }
  2142.     }
  2143.     return s;
  2144. }
  2145. //
  2146. //  Do estimates for standard deviations for per-frame
  2147. //  statistics
  2148. //
  2149. HRESULT CBaseVideoRenderer::GetStdDev(
  2150.     int nSamples,
  2151.     int *piResult,
  2152.     LONGLONG llSumSq,
  2153.     LONGLONG iTot
  2154. )
  2155. {
  2156.     CheckPointer(piResult,E_POINTER);
  2157.     CAutoLock cVideoLock(&m_InterfaceLock);
  2158.     if (NULL==m_pClock) {
  2159.         *piResult = 0;
  2160.         return NOERROR;
  2161.     }
  2162.     // If S is the Sum of the Squares of observations and
  2163.     //    T the Total (i.e. sum) of the observations and there were
  2164.     //    N observations, then an estimate of the standard deviation is
  2165.     //      sqrt( (S - T**2/N) / (N-1) )
  2166.     if (nSamples<=1) {
  2167.         *piResult = 0;
  2168.     } else {
  2169.         LONGLONG x;
  2170.         // First frames have invalid stamps, so we get no stats for them
  2171.         // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
  2172.         // so we use m_cFramesDrawn-1 here
  2173.         x = llSumSq - llMulDiv(iTot, iTot, nSamples, 0);
  2174.         x = x / (nSamples-1);
  2175.         ASSERT(x>=0);
  2176.         *piResult = isqrt((LONG)x);
  2177.     }
  2178.     return NOERROR;
  2179. }
  2180. // Set *piDev to the standard deviation in mSec of the sync offset
  2181. // of each frame since streaming started.
  2182. STDMETHODIMP CBaseVideoRenderer::get_DevSyncOffset( int *piDev)
  2183. {
  2184.     // First frames have invalid stamps, so we get no stats for them
  2185.     // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1
  2186.     return GetStdDev(m_cFramesDrawn - 1,
  2187.                      piDev,
  2188.                      m_iSumSqAcc,
  2189.                      m_iTotAcc);
  2190. } // get_DevSyncOffset
  2191. // Set *piJitter to the standard deviation in mSec of the inter-frame time
  2192. // of frames since streaming started.
  2193. STDMETHODIMP CBaseVideoRenderer::get_Jitter( int *piJitter)
  2194. {
  2195.     // First frames have invalid stamps, so we get no stats for them
  2196.     // So second frame gives invalid inter-frame time
  2197.     // So we need 3 frames to get 1 datum, so N is cFramesDrawn-2
  2198.     return GetStdDev(m_cFramesDrawn - 2,
  2199.                      piJitter,
  2200.                      m_iSumSqFrameTime,
  2201.                      m_iSumFrameTime);
  2202. } // get_Jitter
  2203. // Overidden to return our IQualProp interface
  2204. STDMETHODIMP
  2205. CBaseVideoRenderer::NonDelegatingQueryInterface(REFIID riid,VOID **ppv)
  2206. {
  2207.     // We return IQualProp and delegate everything else
  2208.     if (riid == IID_IQualProp) {
  2209.         return GetInterface( (IQualProp *)this, ppv);
  2210.     } else if (riid == IID_IQualityControl) {
  2211.         return GetInterface( (IQualityControl *)this, ppv);
  2212.     }
  2213.     return CBaseRenderer::NonDelegatingQueryInterface(riid,ppv);
  2214. }
  2215. // Override JoinFilterGraph so that, just before leaving
  2216. // the graph we can send an EC_WINDOW_DESTROYED event
  2217. STDMETHODIMP
  2218. CBaseVideoRenderer::JoinFilterGraph(IFilterGraph *pGraph,LPCWSTR pName)
  2219. {
  2220.     // Since we send EC_ACTIVATE, we also need to ensure
  2221.     // we send EC_WINDOW_DESTROYED or the resource manager may be
  2222.     // holding us as a focus object
  2223.     if (!pGraph && m_pGraph) {
  2224.         // We were in a graph and now we're not
  2225.         // Do this properly in case we are aggregated
  2226.         IBaseFilter* pFilter;
  2227.         QueryInterface(IID_IBaseFilter,(void **) &pFilter);
  2228.         NotifyEvent(EC_WINDOW_DESTROYED, (LPARAM) pFilter, 0);
  2229.         pFilter->Release();
  2230.     }
  2231.     return CBaseFilter::JoinFilterGraph(pGraph, pName);
  2232. }
  2233. // This removes a large number of level 4 warnings from the
  2234. // Microsoft compiler which in this case are not very useful
  2235. #pragma warning(disable: 4514)