1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes.
6 // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
12 // #include <vtransfr.h> // now in precomp file streams.h
14 CVideoTransformFilter::CVideoTransformFilter
15 ( TCHAR
*pName
, LPUNKNOWN pUnk
, REFCLSID clsid
)
16 : CTransformFilter(pName
, pUnk
, clsid
)
18 , m_nKeyFramePeriod(0) // No QM until we see at least 2 key frames
19 , m_nFramesSinceKeyFrame(0)
22 , m_itrAvgDecode(300000) // 30mSec - probably allows skipping
23 , m_bQualityChanged(FALSE
)
31 CVideoTransformFilter::~CVideoTransformFilter()
37 // Reset our quality management state
39 HRESULT
CVideoTransformFilter::StartStreaming()
42 m_nKeyFramePeriod
= 0; // No QM until we see at least 2 key frames
43 m_nFramesSinceKeyFrame
= 0;
46 m_itrAvgDecode
= 300000; // 30mSec - probably allows skipping
47 m_bQualityChanged
= FALSE
;
48 m_bSampleSkipped
= FALSE
;
53 // Overriden to reset quality management information
55 HRESULT
CVideoTransformFilter::EndFlush()
59 CAutoLock
lck(&m_csReceive
);
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
66 CVideoTransformFilter::StartStreaming();
68 return CTransformFilter::EndFlush();
72 HRESULT
CVideoTransformFilter::AbortPlayback(HRESULT hr
)
74 NotifyEvent(EC_ERRORABORT
, hr
, 0);
75 m_pOutput
->DeliverEndOfStream();
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.
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.
98 HRESULT
CVideoTransformFilter::Receive(IMediaSample
*pSample
)
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.
111 ASSERT(CritCheckIn(&m_csReceive
));
112 AM_MEDIA_TYPE
*pmtOut
, *pmt
;
118 IMediaSample
* pOutSample
;
120 // If no output pin to deliver to then no point sending us data
121 ASSERT (m_pOutput
!= NULL
) ;
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)
129 #define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource
130 #define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget
132 pSample
->GetMediaType(&pmt
);
133 if (pmt
!= NULL
&& pmt
->pbFormat
!= NULL
) {
135 // spew some debug output
136 ASSERT(!IsEqualGUID(pmt
->majortype
, GUID_NULL
));
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
,
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.
159 m_pInput
->CurrentMediaType() = *pmt
;
160 DeleteMediaType(pmt
);
161 // if this fails, playback will stop, so signal an error
162 hr
= StartStreaming();
164 return AbortPlayback(hr
);
168 // Now that we have noticed any format changes on the input sample, it's
171 if (ShouldSkipFrame(pSample
)) {
173 m_bSampleSkipped
= TRUE
;
177 // Set up the output sample
178 hr
= InitializeOutputSample(pSample
, &pOutSample
);
184 m_bSampleSkipped
= FALSE
;
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
189 #define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource
190 #define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget
192 pOutSample
->GetMediaType(&pmtOut
);
193 if (pmtOut
!= NULL
&& pmtOut
->pbFormat
!= NULL
) {
195 // spew some debug output
196 ASSERT(!IsEqualGUID(pmtOut
->majortype
, GUID_NULL
));
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
,
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.
219 m_pOutput
->CurrentMediaType() = *pmtOut
;
220 DeleteMediaType(pmtOut
);
221 hr
= StartStreaming();
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")));
230 // if this fails, playback will stop, so signal an error
233 // Must release the sample before calling AbortPlayback
234 // because we might be holding the win16 lock or
236 pOutSample
->Release();
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")));
248 // Start timing the transform (and log it if PERF is defined)
251 m_tDecodeStart
= timeGetTime();
252 MSR_START(m_idTransform
);
254 // have the derived class transform the data
255 hr
= Transform(pSample
, pOutSample
);
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);
262 // Maybe we're waiting for a keyframe still?
265 if (m_nWaitForKey
&& pSample
->IsSyncPoint() == S_OK
)
266 m_nWaitForKey
= FALSE
;
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")));
276 DbgLog((LOG_TRACE
,1,TEXT("Error from video transform")));
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.
285 hr
= m_pOutput
->Deliver(pOutSample
);
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
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);
308 // release the output buffer. If the connected pin still needs it,
309 // it will have addrefed it itself.
310 pOutSample
->Release();
311 ASSERT(CritCheckIn(&m_csReceive
));
318 BOOL
CVideoTransformFilter::ShouldSkipFrame( IMediaSample
* pIn
)
320 REFERENCE_TIME trStart
, trStopAt
;
321 HRESULT hr
= pIn
->GetTime(&trStart
, &trStopAt
);
323 // Don't skip frames with no timestamps
327 int itrFrame
= (int)(trStopAt
- trStart
); // frame duration
329 if(S_OK
==pIn
->IsSyncPoint()) {
330 MSR_INTEGER(m_idFrameType
, 1);
331 if ( m_nKeyFramePeriod
< m_nFramesSinceKeyFrame
) {
333 m_nKeyFramePeriod
= m_nFramesSinceKeyFrame
;
335 m_nFramesSinceKeyFrame
= 0;
338 MSR_INTEGER(m_idFrameType
, 2);
339 if ( m_nFramesSinceKeyFrame
>m_nKeyFramePeriod
340 && m_nKeyFramePeriod
>0
342 // We haven't seen the key frame yet, but we were clearly being
343 // overoptimistic about how frequent they are.
344 m_nKeyFramePeriod
= m_nFramesSinceKeyFrame
;
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
) {
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
) {
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.
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
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)
379 * (m_nKeyFramePeriod
-m_nFramesSinceKeyFrame
- 1);
380 MSR_INTEGER(m_idTimeTillKey
, it
);
382 // For debug - might want to see the details - dump them as scratch pad
384 MSR_INTEGER(0, itrFrame
);
385 MSR_INTEGER(0, m_nFramesSinceKeyFrame
);
386 MSR_INTEGER(0, m_nKeyFramePeriod
);
388 if (m_itrLate
/10000 > it
) {
390 // Now we are committed. Once we start skipping, we
391 // cannot stop until we hit a key frame.
394 MSR_INTEGER(0, 777770); // not near enough to next key
399 MSR_INTEGER(0, 777771); // Next key not predictable
404 MSR_INTEGER(0, 777772); // Less than one frame late
405 MSR_INTEGER(0, m_itrLate
);
406 MSR_INTEGER(0, itrFrame
);
411 MSR_INTEGER(0, 777773); // Decode time short - not not worth skipping
412 MSR_INTEGER(0, m_itrAvgDecode
);
413 MSR_INTEGER(0, itrFrame
);
417 ++m_nFramesSinceKeyFrame
;
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
;
430 MSR_INTEGER(m_idLate
, (int)m_itrLate
/10000 ); // Note how late we think we are
432 if (!m_bQualityChanged
) {
433 m_bQualityChanged
= TRUE
;
434 NotifyEvent(EC_QUALITY_CHANGE
,0,0);
441 HRESULT
CVideoTransformFilter::AlterQuality(Quality q
)
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;
449 m_itrLate
= (int)q
.Late
;
451 // We ignore the other fields
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.
460 return E_FAIL
; // Tell the renderer to do his thing.
466 // This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4
467 #pragma warning(disable:4514)