A Fast Bresenham Type Algorithm For Drawing Ellipses by John Kennedy
[xy_vsfilter.git] / src / filters / BaseClasses / vtrans.cpp
blob26f081dfa28d45f2e33c394ae6997e365b347c23
1 //------------------------------------------------------------------------------
2 // File: Vtrans.cpp
3 //
4 // Desc: DirectShow base classes.
5 //
6 // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
10 #include <streams.h>
11 #include <measure.h>
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)
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)
25 #ifdef PERF
26 RegisterPerfId();
27 #endif // PERF
31 CVideoTransformFilter::~CVideoTransformFilter()
33 // nothing to do
37 // Reset our quality management state
39 HRESULT CVideoTransformFilter::StartStreaming()
41 m_itrLate = 0;
42 m_nKeyFramePeriod = 0; // No QM until we see at least 2 key frames
43 m_nFramesSinceKeyFrame = 0;
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;
53 // Overriden to reset quality management information
55 HRESULT CVideoTransformFilter::EndFlush()
58 // Synchronize
59 CAutoLock lck(&m_csReceive);
61 // Reset our stats
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();
76 return hr;
80 // Receive()
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;
113 #ifdef DEBUG
114 FOURCCMap fccOut;
115 #endif
116 HRESULT hr;
117 ASSERT(pSample);
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));
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
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.
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);
168 // Now that we have noticed any format changes on the input sample, it's
169 // OK to discard it.
171 if (ShouldSkipFrame(pSample)) {
172 MSR_NOTE(m_idSkip);
173 m_bSampleSkipped = TRUE;
174 return NOERROR;
177 // Set up the output sample
178 hr = InitializeOutputSample(pSample, &pOutSample);
180 if (FAILED(hr)) {
181 return hr;
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));
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
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.
218 StopStreaming();
219 m_pOutput->CurrentMediaType() = *pmtOut;
220 DeleteMediaType(pmtOut);
221 hr = StartStreaming();
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;
230 // if this fails, playback will stop, so signal an error
231 } else {
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;
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;
248 // Start timing the transform (and log it if PERF is defined)
250 if (SUCCEEDED(hr)) {
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?
263 if (m_nWaitForKey)
264 m_nWaitForKey--;
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")));
271 hr = S_FALSE;
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.
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) {
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);
303 return NOERROR;
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));
313 return hr;
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
324 if (hr != S_OK)
325 return FALSE;
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 ) {
332 // record the max
333 m_nKeyFramePeriod = m_nFramesSinceKeyFrame;
335 m_nFramesSinceKeyFrame = 0;
336 m_bSkipping = FALSE;
337 } else {
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
383 #ifdef VTRANSPERF
384 MSR_INTEGER(0, itrFrame);
385 MSR_INTEGER(0, m_nFramesSinceKeyFrame);
386 MSR_INTEGER(0, m_nKeyFramePeriod);
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
397 } else {
398 #ifdef VTRANSPERF
399 MSR_INTEGER(0, 777771); // Next key not predictable
400 #endif
402 } else {
403 #ifdef VTRANSPERF
404 MSR_INTEGER(0, 777772); // Less than one frame late
405 MSR_INTEGER(0, m_itrLate);
406 MSR_INTEGER(0, itrFrame);
407 #endif
409 } else {
410 #ifdef VTRANSPERF
411 MSR_INTEGER(0, 777773); // Decode time short - not not worth skipping
412 MSR_INTEGER(0, m_itrAvgDecode);
413 MSR_INTEGER(0, itrFrame);
414 #endif
417 ++m_nFramesSinceKeyFrame;
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;
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);
437 return m_bSkipping;
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;
448 } else {
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)