home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Game Programming for Teens / VBGPFT.cdr / DirectX8 / dx8a_sdk.exe / samples / multimedia / directshow / baseclasses / renbase.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  89.6 KB  |  2,762 lines

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