AJA NTV2 SDK  18.0.0.2717
NTV2 SDK 18.0.0.2717
vtrans.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // File: Vtrans.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>
11 #include <measure.h>
12 // #include <vtransfr.h> // now in precomp file streams.h
13 
15  ( __in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, REFCLSID clsid)
16  : CTransformFilter(pName, pUnk, clsid)
17  , m_itrLate(0)
18  , m_nKeyFramePeriod(0) // No QM until we see at least 2 key frames
19  , m_nFramesSinceKeyFrame(0)
20  , m_bSkipping(FALSE)
21  , m_tDecodeStart(0)
22  , m_itrAvgDecode(300000) // 30mSec - probably allows skipping
23  , m_bQualityChanged(FALSE)
24 {
25 #ifdef PERF
26  RegisterPerfId();
27 #endif // PERF
28 }
29 
30 
32 {
33  // nothing to do
34 }
35 
36 
37 // Reset our quality management state
38 
40 {
41  m_itrLate = 0;
42  m_nKeyFramePeriod = 0; // No QM until we see at least 2 key frames
44  m_bSkipping = FALSE;
45  m_tDecodeStart = 0;
46  m_itrAvgDecode = 300000; // 30mSec - probably allows skipping
47  m_bQualityChanged = FALSE;
48  m_bSampleSkipped = FALSE;
49  return NOERROR;
50 }
51 
52 
53 // Overriden to reset quality management information
54 
56 {
57  {
58  // Synchronize
59  CAutoLock lck(&m_csReceive);
60 
61  // Reset our stats
62  //
63  // Note - we don't want to call derived classes here,
64  // we only want to reset our internal variables and this
65  // is a convenient way to do it
67  }
69 }
70 
71 
73 {
74  NotifyEvent(EC_ERRORABORT, hr, 0);
76  return hr;
77 }
78 
79 
80 // Receive()
81 //
82 // Accept a sample from upstream, decide whether to process it
83 // or drop it. If we process it then get a buffer from the
84 // allocator of the downstream connection, transform it into the
85 // new buffer and deliver it to the downstream filter.
86 // If we decide not to process it then we do not get a buffer.
87 
88 // Remember that although this code will notice format changes coming into
89 // the input pin, it will NOT change its output format if that results
90 // in the filter needing to make a corresponding output format change. Your
91 // derived filter will have to take care of that. (eg. a palette change if
92 // the input and output is an 8 bit format). If the input sample is discarded
93 // and nothing is sent out for this Receive, please remember to put the format
94 // change on the first output sample that you actually do send.
95 // If your filter will produce the same output type even when the input type
96 // changes, then this base class code will do everything you need.
97 
98 HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample)
99 {
100  // If the next filter downstream is the video renderer, then it may
101  // be able to operate in DirectDraw mode which saves copying the data
102  // and gives higher performance. In that case the buffer which we
103  // get from GetDeliveryBuffer will be a DirectDraw buffer, and
104  // drawing into this buffer draws directly onto the display surface.
105  // This means that any waiting for the correct time to draw occurs
106  // during GetDeliveryBuffer, and that once the buffer is given to us
107  // the video renderer will count it in its statistics as a frame drawn.
108  // This means that any decision to drop the frame must be taken before
109  // calling GetDeliveryBuffer.
110 
112  AM_MEDIA_TYPE *pmtOut, *pmt;
113 #ifdef DEBUG
114  FOURCCMap fccOut;
115 #endif
116  HRESULT hr;
117  ASSERT(pSample);
118  IMediaSample * pOutSample;
119 
120  // If no output pin to deliver to then no point sending us data
121  ASSERT (m_pOutput != NULL) ;
122 
123  // The source filter may dynamically ask us to start transforming from a
124  // different media type than the one we're using now. If we don't, we'll
125  // draw garbage. (typically, this is a palette change in the movie,
126  // but could be something more sinister like the compression type changing,
127  // or even the video size changing)
128 
129 #define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource
130 #define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget
131 
132  pSample->GetMediaType(&pmt);
133  if (pmt != NULL && pmt->pbFormat != NULL) {
134 
135  // spew some debug output
136  ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL));
137 #ifdef DEBUG
138  fccOut.SetFOURCC(&pmt->subtype);
139  LONG lCompression = HEADER(pmt->pbFormat)->biCompression;
140  LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount;
141  LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8;
142  lStride = (lStride + 3) & ~3;
143  DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to")));
144  DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
145  fccOut.GetFOURCC(), lCompression, lBitCount));
146  DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
147  HEADER(pmt->pbFormat)->biHeight,
148  rcT1.left, rcT1.top, rcT1.right, rcT1.bottom));
149  DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
150  rcS1.left, rcS1.top, rcS1.right, rcS1.bottom,
151  lStride));
152 #endif
153 
154  // now switch to using the new format. I am assuming that the
155  // derived filter will do the right thing when its media type is
156  // switched and streaming is restarted.
157 
158  StopStreaming();
159  m_pInput->CurrentMediaType() = *pmt;
160  DeleteMediaType(pmt);
161  // if this fails, playback will stop, so signal an error
162  hr = StartStreaming();
163  if (FAILED(hr)) {
164  return AbortPlayback(hr);
165  }
166  }
167 
168  // Now that we have noticed any format changes on the input sample, it's
169  // OK to discard it.
170 
171  if (ShouldSkipFrame(pSample)) {
172  MSR_NOTE(m_idSkip);
173  m_bSampleSkipped = TRUE;
174  return NOERROR;
175  }
176 
177  // Set up the output sample
178  hr = InitializeOutputSample(pSample, &pOutSample);
179 
180  if (FAILED(hr)) {
181  return hr;
182  }
183 
184  m_bSampleSkipped = FALSE;
185 
186  // The renderer may ask us to on-the-fly to start transforming to a
187  // different format. If we don't obey it, we'll draw garbage
188 
189 #define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource
190 #define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget
191 
192  pOutSample->GetMediaType(&pmtOut);
193  if (pmtOut != NULL && pmtOut->pbFormat != NULL) {
194 
195  // spew some debug output
196  ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL));
197 #ifdef DEBUG
198  fccOut.SetFOURCC(&pmtOut->subtype);
199  LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression;
200  LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount;
201  LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8;
202  lStride = (lStride + 3) & ~3;
203  DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to")));
204  DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"),
205  fccOut.GetFOURCC(), lCompression, lBitCount));
206  DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"),
207  HEADER(pmtOut->pbFormat)->biHeight,
208  rcT.left, rcT.top, rcT.right, rcT.bottom));
209  DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"),
210  rcS.left, rcS.top, rcS.right, rcS.bottom,
211  lStride));
212 #endif
213 
214  // now switch to using the new format. I am assuming that the
215  // derived filter will do the right thing when its media type is
216  // switched and streaming is restarted.
217 
218  StopStreaming();
219  m_pOutput->CurrentMediaType() = *pmtOut;
220  DeleteMediaType(pmtOut);
221  hr = StartStreaming();
222 
223  if (SUCCEEDED(hr)) {
224  // a new format, means a new empty buffer, so wait for a keyframe
225  // before passing anything on to the renderer.
226  // !!! a keyframe may never come, so give up after 30 frames
227  DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe")));
228  m_nWaitForKey = 30;
229 
230  // if this fails, playback will stop, so signal an error
231  } else {
232 
233  // Must release the sample before calling AbortPlayback
234  // because we might be holding the win16 lock or
235  // ddraw lock
236  pOutSample->Release();
237  AbortPlayback(hr);
238  return hr;
239  }
240  }
241 
242  // After a discontinuity, we need to wait for the next key frame
243  if (pSample->IsDiscontinuity() == S_OK) {
244  DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe")));
245  m_nWaitForKey = 30;
246  }
247 
248  // Start timing the transform (and log it if PERF is defined)
249 
250  if (SUCCEEDED(hr)) {
251  m_tDecodeStart = timeGetTime();
252  MSR_START(m_idTransform);
253 
254  // have the derived class transform the data
255  hr = Transform(pSample, pOutSample);
256 
257  // Stop the clock (and log it if PERF is defined)
258  MSR_STOP(m_idTransform);
259  m_tDecodeStart = timeGetTime()-m_tDecodeStart;
260  m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16);
261 
262  // Maybe we're waiting for a keyframe still?
263  if (m_nWaitForKey)
264  m_nWaitForKey--;
265  if (m_nWaitForKey && pSample->IsSyncPoint() == S_OK)
266  m_nWaitForKey = FALSE;
267 
268  // if so, then we don't want to pass this on to the renderer
269  if (m_nWaitForKey && hr == NOERROR) {
270  DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe")));
271  hr = S_FALSE;
272  }
273  }
274 
275  if (FAILED(hr)) {
276  DbgLog((LOG_TRACE,1,TEXT("Error from video transform")));
277  } else {
278  // the Transform() function can return S_FALSE to indicate that the
279  // sample should not be delivered; we only deliver the sample if it's
280  // really S_OK (same as NOERROR, of course.)
281  // Try not to return S_FALSE to a direct draw buffer (it's wasteful)
282  // Try to take the decision earlier - before you get it.
283 
284  if (hr == NOERROR) {
285  hr = m_pOutput->Deliver(pOutSample);
286  } else {
287  // S_FALSE returned from Transform is a PRIVATE agreement
288  // We should return NOERROR from Receive() in this case because returning S_FALSE
289  // from Receive() means that this is the end of the stream and no more data should
290  // be sent.
291  if (S_FALSE == hr) {
292 
293  // We must Release() the sample before doing anything
294  // like calling the filter graph because having the
295  // sample means we may have the DirectDraw lock
296  // (== win16 lock on some versions)
297  pOutSample->Release();
298  m_bSampleSkipped = TRUE;
299  if (!m_bQualityChanged) {
300  m_bQualityChanged = TRUE;
301  NotifyEvent(EC_QUALITY_CHANGE,0,0);
302  }
303  return NOERROR;
304  }
305  }
306  }
307 
308  // release the output buffer. If the connected pin still needs it,
309  // it will have addrefed it itself.
310  pOutSample->Release();
312 
313  return hr;
314 }
315 
316 
317 
318 BOOL CVideoTransformFilter::ShouldSkipFrame( IMediaSample * pIn)
319 {
320  REFERENCE_TIME trStart, trStopAt;
321  HRESULT hr = pIn->GetTime(&trStart, &trStopAt);
322 
323  // Don't skip frames with no timestamps
324  if (hr != S_OK)
325  return FALSE;
326 
327  int itrFrame = (int)(trStopAt - trStart); // frame duration
328 
329  if(S_OK==pIn->IsSyncPoint()) {
330  MSR_INTEGER(m_idFrameType, 1);
332  // record the max
334  }
336  m_bSkipping = FALSE;
337  } else {
338  MSR_INTEGER(m_idFrameType, 2);
340  && m_nKeyFramePeriod>0
341  ) {
342  // We haven't seen the key frame yet, but we were clearly being
343  // overoptimistic about how frequent they are.
345  }
346  }
347 
348 
349  // Whatever we might otherwise decide,
350  // if we are taking only a small fraction of the required frame time to decode
351  // then any quality problems are actually coming from somewhere else.
352  // Could be a net problem at the source for instance. In this case there's
353  // no point in us skipping frames here.
354  if (m_itrAvgDecode*4>itrFrame) {
355 
356  // Don't skip unless we are at least a whole frame late.
357  // (We would skip B frames if more than 1/2 frame late, but they're safe).
358  if ( m_itrLate > itrFrame ) {
359 
360  // Don't skip unless the anticipated key frame would be no more than
361  // 1 frame early. If the renderer has not been waiting (we *guess*
362  // it hasn't because we're late) then it will allow frames to be
363  // played early by up to a frame.
364 
365  // Let T = Stream time from now to anticipated next key frame
366  // = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame)
367  // So we skip if T - Late < one frame i.e.
368  // (duration) * (freq - FramesSince) - Late < duration
369  // or (duration) * (freq - FramesSince - 1) < Late
370 
371  // We don't dare skip until we have seen some key frames and have
372  // some idea how often they occur and they are reasonably frequent.
373  if (m_nKeyFramePeriod>0) {
374  // It would be crazy - but we could have a stream with key frames
375  // a very long way apart - and if they are further than about
376  // 3.5 minutes apart then we could get arithmetic overflow in
377  // reference time units. Therefore we switch to mSec at this point
378  int it = (itrFrame/10000)
380  MSR_INTEGER(m_idTimeTillKey, it);
381 
382  // For debug - might want to see the details - dump them as scratch pad
383 #ifdef VTRANSPERF
384  MSR_INTEGER(0, itrFrame);
387 #endif
388  if (m_itrLate/10000 > it) {
389  m_bSkipping = TRUE;
390  // Now we are committed. Once we start skipping, we
391  // cannot stop until we hit a key frame.
392  } else {
393 #ifdef VTRANSPERF
394  MSR_INTEGER(0, 777770); // not near enough to next key
395 #endif
396  }
397  } else {
398 #ifdef VTRANSPERF
399  MSR_INTEGER(0, 777771); // Next key not predictable
400 #endif
401  }
402  } else {
403 #ifdef VTRANSPERF
404  MSR_INTEGER(0, 777772); // Less than one frame late
406  MSR_INTEGER(0, itrFrame);
407 #endif
408  }
409  } else {
410 #ifdef VTRANSPERF
411  MSR_INTEGER(0, 777773); // Decode time short - not not worth skipping
413  MSR_INTEGER(0, itrFrame);
414 #endif
415  }
416 
418 
419  if (m_bSkipping) {
420  // We will count down the lateness as we skip each frame.
421  // We re-assess each frame. The key frame might not arrive when expected.
422  // We reset m_itrLate if we get a new Quality message, but actually that's
423  // not likely because we're not sending frames on to the Renderer. In
424  // fact if we DID get another one it would mean that there's a long
425  // pipe between us and the renderer and we might need an altogether
426  // better strategy to avoid hunting!
427  m_itrLate = m_itrLate - itrFrame;
428  }
429 
430  MSR_INTEGER(m_idLate, (int)m_itrLate/10000 ); // Note how late we think we are
431  if (m_bSkipping) {
432  if (!m_bQualityChanged) {
433  m_bQualityChanged = TRUE;
434  NotifyEvent(EC_QUALITY_CHANGE,0,0);
435  }
436  }
437  return m_bSkipping;
438 }
439 
440 
442 {
443  // to reduce the amount of 64 bit arithmetic, m_itrLate is an int.
444  // +, -, >, == etc are not too bad, but * and / are painful.
445  if (m_itrLate>300000000) {
446  // Avoid overflow and silliness - more than 30 secs late is already silly
447  m_itrLate = 300000000;
448  } else {
449  m_itrLate = (int)q.Late;
450  }
451  // We ignore the other fields
452 
453  // We're actually not very good at handling this. In non-direct draw mode
454  // most of the time can be spent in the renderer which can skip any frame.
455  // In that case we'd rather the renderer handled things.
456  // Nevertheless we will keep an eye on it and if we really start getting
457  // a very long way behind then we will actually skip - but we'll still tell
458  // the renderer (or whoever is downstream) that they should handle quality.
459 
460  return E_FAIL; // Tell the renderer to do his thing.
461 
462 }
463 
464 
465 
466 // This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4
467 #pragma warning(disable:4514)
468 
CTransformFilter::m_pOutput
CTransformOutputPin * m_pOutput
Definition: transfrm.h:299
CBaseFilter::NotifyEvent
HRESULT NotifyEvent(long EventCode, LONG_PTR EventParam1, LONG_PTR EventParam2)
Definition: amfilter.cpp:811
CritCheckIn
#define CritCheckIn(x)
Definition: wxutil.h:75
CVideoTransformFilter::m_nKeyFramePeriod
int m_nKeyFramePeriod
Definition: vtrans.h:103
FOURCCMap::GetFOURCC
DWORD GetFOURCC(void)
Definition: fourcc.h:95
CVideoTransformFilter::m_bSkipping
BOOL m_bSkipping
Definition: vtrans.h:109
CVideoTransformFilter::m_tDecodeStart
int m_tDecodeStart
Definition: vtrans.h:130
streams.h
NULL
#define NULL
Definition: ntv2caption608types.h:19
CTransformFilter::m_pInput
CTransformInputPin * m_pInput
Definition: transfrm.h:298
MSR_STOP
#define MSR_STOP(a)
Definition: measure.h:138
CTransformOutputPin::CurrentMediaType
CMediaType & CurrentMediaType()
Definition: transfrm.h:170
LOG_TRACE
@ LOG_TRACE
Definition: wxdebug.h:45
CVideoTransformFilter::CVideoTransformFilter
CVideoTransformFilter(__in_opt LPCTSTR, __inout_opt LPUNKNOWN, REFCLSID clsid)
Definition: vtrans.cpp:15
CBaseOutputPin::Deliver
virtual HRESULT Deliver(IMediaSample *)
Definition: amfilter.cpp:2685
CAutoLock
Definition: wxutil.h:83
CVideoTransformFilter::~CVideoTransformFilter
~CVideoTransformFilter()
Definition: vtrans.cpp:31
CVideoTransformFilter::EndFlush
HRESULT EndFlush()
Definition: vtrans.cpp:55
CTransformFilter::m_csReceive
CCritSec m_csReceive
Definition: transfrm.h:292
CVideoTransformFilter::Receive
HRESULT Receive(IMediaSample *pSample)
Definition: vtrans.cpp:98
CVideoTransformFilter::m_itrAvgDecode
int m_itrAvgDecode
Definition: vtrans.h:131
rcS1
#define rcS1
MSR_START
#define MSR_START(a)
Definition: measure.h:137
CTransformFilter::StopStreaming
virtual HRESULT StopStreaming()
Definition: transfrm.cpp:179
pName
CHAR * pName
Definition: amvideo.cpp:26
DbgLog
#define DbgLog(_x_)
Definition: wxdebug.h:183
rcT
#define rcT
FOURCCMap::SetFOURCC
void SetFOURCC(DWORD fourcc)
Definition: fourcc.h:89
CVideoTransformFilter::m_nFramesSinceKeyFrame
int m_nFramesSinceKeyFrame
Definition: vtrans.h:106
DeleteMediaType
void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt)
Definition: mtype.cpp:353
CVideoTransformFilter::m_itrLate
int m_itrLate
Definition: vtrans.h:128
rcS
#define rcS
CVideoTransformFilter::ShouldSkipFrame
BOOL ShouldSkipFrame(IMediaSample *pIn)
Definition: vtrans.cpp:318
CBaseOutputPin::DeliverEndOfStream
virtual HRESULT DeliverEndOfStream(void)
Definition: amfilter.cpp:2702
CTransformFilter::Transform
virtual HRESULT Transform(IMediaSample *pIn, IMediaSample *pOut)
Definition: transfrm.cpp:63
MSR_NOTE
#define MSR_NOTE(a)
Definition: measure.h:139
MSR_INTEGER
#define MSR_INTEGER(a, b)
Definition: measure.h:140
CVideoTransformFilter::StartStreaming
virtual HRESULT StartStreaming()
Definition: vtrans.cpp:39
CTransformInputPin::CurrentMediaType
CMediaType & CurrentMediaType()
Definition: transfrm.h:100
CTransformFilter::InitializeOutputSample
HRESULT InitializeOutputSample(IMediaSample *pSample, __deref_out IMediaSample **ppOutSample)
Definition: transfrm.cpp:230
rcT1
#define rcT1
FOURCCMap
Definition: fourcc.h:31
CTransformFilter::EndFlush
virtual HRESULT EndFlush(void)
Definition: transfrm.cpp:429
CTransformFilter::m_bSampleSkipped
BOOL m_bSampleSkipped
Definition: transfrm.h:276
CVideoTransformFilter::m_nWaitForKey
int m_nWaitForKey
Definition: vtrans.h:142
CVideoTransformFilter::AlterQuality
HRESULT AlterQuality(Quality q)
Definition: vtrans.cpp:441
CVideoTransformFilter::AbortPlayback
HRESULT AbortPlayback(HRESULT hr)
Definition: vtrans.cpp:72
ASSERT
#define ASSERT(_x_)
Definition: wxdebug.h:205
hr
__out HRESULT & hr
Definition: pstream.cpp:145
CTransformFilter
Definition: transfrm.h:174
measure.h
CVideoTransformFilter::m_bQualityChanged
BOOL m_bQualityChanged
Definition: vtrans.h:138