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