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