A Fast Bresenham Type Algorithm For Drawing Ellipses by John Kennedy
[xy_vsfilter.git] / src / filters / BaseClasses / strmctl.cpp
blob1eaf0b58b253e147d1afd21434e549f5ba02b869
1 //------------------------------------------------------------------------------
2 // File: StrmCtl.cpp
3 //
4 // Desc: DirectShow base classes.
5 //
6 // Copyright (c) 1996-2002 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
10 #include <streams.h>
11 #include <strmctl.h>
13 CBaseStreamControl::CBaseStreamControl()
14 : m_StreamState(STREAM_FLOWING)
15 , m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop
16 , m_tStartTime(MAX_TIME)
17 , m_tStopTime(MAX_TIME)
18 , m_dwStartCookie(0)
19 , m_dwStopCookie(0)
20 , m_pRefClock(NULL)
21 , m_FilterState(State_Stopped)
22 , m_bIsFlushing(FALSE)
23 , m_bStopSendExtra(FALSE)
26 CBaseStreamControl::~CBaseStreamControl()
28 // Make sure we release the clock.
29 SetSyncSource(NULL);
30 return;
34 STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie)
36 CAutoLock lck(&m_CritSec);
37 m_bStopSendExtra = FALSE; // reset
38 m_bStopExtraSent = FALSE;
39 if (ptStop)
41 if (*ptStop == MAX_TIME)
43 DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));
44 CancelStop();
45 // If there's now a command to start in the future, we assume
46 // they want to be stopped when the graph is first run
47 if (m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {
48 m_StreamState = STREAM_DISCARDING;
49 DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
51 return NOERROR;
53 DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),
54 (int)(*ptStop/10000), bSendExtra));
55 // if the first command is to stop in the future, then we assume they
56 // want to be started when the graph is first run
57 if (m_FilterState == State_Stopped && m_tStartTime > *ptStop) {
58 m_StreamState = STREAM_FLOWING;
59 DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
61 m_bStopSendExtra = bSendExtra;
62 m_tStopTime = *ptStop;
63 m_dwStopCookie = dwCookie;
64 m_StreamStateOnStop = STREAM_DISCARDING;
66 else
68 DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));
69 // sending an extra frame when told to stop now would mess people up
70 m_bStopSendExtra = FALSE;
71 m_tStopTime = MAX_TIME;
72 m_dwStopCookie = 0;
73 m_StreamState = STREAM_DISCARDING;
74 m_StreamStateOnStop = STREAM_FLOWING; // no pending stop
76 // we might change our mind what to do with a sample we're blocking
77 m_StreamEvent.Set();
78 return NOERROR;
81 STDMETHODIMP CBaseStreamControl::StartAt
82 ( const REFERENCE_TIME *ptStart, DWORD dwCookie )
84 CAutoLock lck(&m_CritSec);
85 if (ptStart)
87 if (*ptStart == MAX_TIME)
89 DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));
90 CancelStart();
91 // If there's now a command to stop in the future, we assume
92 // they want to be started when the graph is first run
93 if (m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {
94 DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
95 m_StreamState = STREAM_FLOWING;
97 return NOERROR;
99 DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));
100 // if the first command is to start in the future, then we assume they
101 // want to be stopped when the graph is first run
102 if (m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {
103 DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
104 m_StreamState = STREAM_DISCARDING;
106 m_tStartTime = *ptStart;
107 m_dwStartCookie = dwCookie;
108 // if (m_tStopTime == m_tStartTime) CancelStop();
110 else
112 DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));
113 m_tStartTime = MAX_TIME;
114 m_dwStartCookie = 0;
115 m_StreamState = STREAM_FLOWING;
117 // we might change our mind what to do with a sample we're blocking
118 m_StreamEvent.Set();
119 return NOERROR;
122 // Retrieve information about current settings
123 STDMETHODIMP CBaseStreamControl::GetInfo(AM_STREAM_INFO *pInfo)
125 if (pInfo == NULL)
126 return E_POINTER;
128 pInfo->tStart = m_tStartTime;
129 pInfo->tStop = m_tStopTime;
130 pInfo->dwStartCookie = m_dwStartCookie;
131 pInfo->dwStopCookie = m_dwStopCookie;
132 pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;
133 pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;
134 pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;
135 switch (m_StreamState) {
136 default:
137 DbgBreak("Invalid stream state");
138 case STREAM_FLOWING:
139 break;
140 case STREAM_DISCARDING:
141 pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;
142 break;
144 return S_OK;
148 void CBaseStreamControl::ExecuteStop()
150 ASSERT(CritCheckIn(&m_CritSec));
151 m_StreamState = m_StreamStateOnStop;
152 if (m_dwStopCookie && m_pSink) {
153 DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),
154 m_dwStopCookie));
155 m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);
157 CancelStop(); // This will do the tidy up
160 void CBaseStreamControl::ExecuteStart()
162 ASSERT(CritCheckIn(&m_CritSec));
163 m_StreamState = STREAM_FLOWING;
164 if (m_dwStartCookie) {
165 DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),
166 m_dwStartCookie));
167 m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);
169 CancelStart(); // This will do the tidy up
172 void CBaseStreamControl::CancelStop()
174 ASSERT(CritCheckIn(&m_CritSec));
175 m_tStopTime = MAX_TIME;
176 m_dwStopCookie = 0;
177 m_StreamStateOnStop = STREAM_FLOWING;
180 void CBaseStreamControl::CancelStart()
182 ASSERT(CritCheckIn(&m_CritSec));
183 m_tStartTime = MAX_TIME;
184 m_dwStartCookie = 0;
188 // This guy will return one of the three StreamControlState's. Here's what the caller
189 // should do for each one:
191 // STREAM_FLOWING: Proceed as usual (render or pass the sample on)
192 // STREAM_DISCARDING: Calculate the time 'til *pSampleStart and wait that long
193 // for the event handle (GetStreamEventHandle()). If the
194 // wait expires, throw the sample away. If the event
195 // fires, call me back, I've changed my mind.
196 // I use pSampleStart (not Stop) so that live sources don't
197 // block for the duration of their samples, since the clock
198 // will always read approximately pSampleStart when called
201 // All through this code, you'll notice the following rules:
202 // - When start and stop time are the same, it's as if start was first
203 // - An event is considered inside the sample when it's >= sample start time
204 // but < sample stop time
205 // - if any part of the sample is supposed to be sent, we'll send the whole
206 // thing since we don't break it into smaller pieces
207 // - If we skip over a start or stop without doing it, we still signal the event
208 // and reset ourselves in case somebody's waiting for the event, and to make
209 // sure we notice that the event is past and should be forgotten
210 // Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):
212 // 1. xo<--> start then stop
213 // 2. ox<--> stop then start
214 // 3. x<o-> start
215 // 4. o<x-> stop then start
216 // 5. x<-->o start
217 // 6. o<-->x stop
218 // 7. <x->o start
219 // 8. <o->x no change
220 // 9. <xo> start
221 // 10. <ox> stop then start
222 // 11. <-->xo no change
223 // 12. <-->ox no change
224 // 13. x<--> start
225 // 14. <x-> start
226 // 15. <-->x no change
227 // 16. o<--> stop
228 // 17. <o-> no change
229 // 18. <-->o no change
230 // 19. <--> no change
233 enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes
234 ( const REFERENCE_TIME * pSampleStart, const REFERENCE_TIME * pSampleStop )
236 CAutoLock lck(&m_CritSec);
238 ASSERT(!m_bIsFlushing);
239 ASSERT(pSampleStart && pSampleStop);
241 // Don't ask me how I came up with the code below to handle all 19 cases
242 // - DannyMi
244 if (m_tStopTime >= *pSampleStart)
246 if (m_tStartTime >= *pSampleStop)
247 return m_StreamState; // cases 8 11 12 15 17 18 19
248 if (m_tStopTime < m_tStartTime)
249 ExecuteStop(); // case 10
250 ExecuteStart(); // cases 3 5 7 9 13 14
251 return m_StreamState;
254 if (m_tStartTime >= *pSampleStop)
256 ExecuteStop(); // cases 6 16
257 return m_StreamState;
260 if (m_tStartTime <= m_tStopTime)
262 ExecuteStart();
263 ExecuteStop();
264 return m_StreamState; // case 1
266 else
268 ExecuteStop();
269 ExecuteStart();
270 return m_StreamState; // cases 2 4
275 enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample )
278 REFERENCE_TIME rtBufferStart, rtBufferStop;
279 const BOOL bNoBufferTimes =
280 pSample == NULL ||
281 FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));
283 StreamControlState state;
284 LONG lWait;
288 // something has to break out of the blocking
289 if (m_bIsFlushing || m_FilterState == State_Stopped)
290 return STREAM_DISCARDING;
292 if (bNoBufferTimes) {
293 // Can't do anything until we get a time stamp
294 state = m_StreamState;
295 break;
296 } else {
297 state = CheckSampleTimes( &rtBufferStart, &rtBufferStop );
298 if (state == STREAM_FLOWING)
299 break;
301 // we aren't supposed to send this, but we've been
302 // told to send one more than we were supposed to
303 // (and the stop isn't still pending and we're streaming)
304 if (m_bStopSendExtra && !m_bStopExtraSent &&
305 m_tStopTime == MAX_TIME &&
306 m_FilterState != State_Stopped) {
307 m_bStopExtraSent = TRUE;
308 DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),
309 m_dwStopCookie));
310 state = STREAM_FLOWING;
311 break;
315 // We're in discarding mode
317 // If we've no clock, discard as fast as we can
318 if (!m_pRefClock) {
319 break;
321 // If we're paused, we can't discard in a timely manner because
322 // there's no such thing as stream times. We must block until
323 // we run or stop, or we'll end up throwing the whole stream away
324 // as quickly as possible
325 } else if (m_FilterState == State_Paused) {
326 lWait = INFINITE;
328 } else {
329 // wait until it's time for the sample until we say "discard"
330 // ("discard in a timely fashion")
331 REFERENCE_TIME rtNow;
332 EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));
333 rtNow -= m_tRunStart; // Into relative ref-time
334 lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms
335 if (lWait < 10) break; // Not worth waiting - discard early
338 } while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);
340 return state;
344 void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart )
346 CAutoLock lck(&m_CritSec);
348 // or we will get confused
349 if (m_FilterState == new_state)
350 return;
352 switch (new_state)
354 case State_Stopped:
356 DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));
358 // execute any pending starts and stops in the right order,
359 // to make sure all notifications get sent, and we end up
360 // in the right state to begin next time (??? why not?)
362 if (m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {
363 ExecuteStart();
364 } else if (m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {
365 ExecuteStop();
366 } else if (m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {
367 if (m_tStartTime <= m_tStopTime) {
368 ExecuteStart();
369 ExecuteStop();
370 } else {
371 ExecuteStop();
372 ExecuteStart();
375 // always start off flowing when the graph starts streaming
376 // unless told otherwise
377 m_StreamState = STREAM_FLOWING;
378 m_FilterState = new_state;
379 break;
381 case State_Running:
383 DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));
385 m_tRunStart = tStart;
386 // fall-through
388 default: // case State_Paused:
389 m_FilterState = new_state;
391 // unblock!
392 m_StreamEvent.Set();
396 void CBaseStreamControl::Flushing(BOOL bInProgress)
398 CAutoLock lck(&m_CritSec);
399 m_bIsFlushing = bInProgress;
400 m_StreamEvent.Set();