1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes.
6 // Copyright (c) 1996-2002 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
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
)
21 , m_FilterState(State_Stopped
)
22 , m_bIsFlushing(FALSE
)
23 , m_bStopSendExtra(FALSE
)
26 CBaseStreamControl::~CBaseStreamControl()
28 // Make sure we release the clock.
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
;
41 if (*ptStop
== MAX_TIME
)
43 DbgLog((LOG_TRACE
,2,TEXT("StopAt: Cancel stop")));
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")));
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
;
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
;
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
81 STDMETHODIMP
CBaseStreamControl::StartAt
82 ( const REFERENCE_TIME
*ptStart
, DWORD dwCookie
)
84 CAutoLock
lck(&m_CritSec
);
87 if (*ptStart
== MAX_TIME
)
89 DbgLog((LOG_TRACE
,2,TEXT("StartAt: Cancel start")));
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
;
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();
112 DbgLog((LOG_TRACE
,2,TEXT("StartAt: now")));
113 m_tStartTime
= MAX_TIME
;
115 m_StreamState
= STREAM_FLOWING
;
117 // we might change our mind what to do with a sample we're blocking
122 // Retrieve information about current settings
123 STDMETHODIMP
CBaseStreamControl::GetInfo(AM_STREAM_INFO
*pInfo
)
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
) {
137 DbgBreak("Invalid stream state");
140 case STREAM_DISCARDING
:
141 pInfo
->dwFlags
|= AM_STREAM_INFO_DISCARDING
;
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)"),
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)"),
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
;
177 m_StreamStateOnStop
= STREAM_FLOWING
;
180 void CBaseStreamControl::CancelStart()
182 ASSERT(CritCheckIn(&m_CritSec
));
183 m_tStartTime
= MAX_TIME
;
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
215 // 4. o<x-> stop then start
219 // 8. <o->x no change
221 // 10. <ox> stop then start
222 // 11. <-->xo no change
223 // 12. <-->ox no change
226 // 15. <-->x no change
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
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
)
264 return m_StreamState
; // case 1
270 return m_StreamState
; // cases 2 4
275 enum CBaseStreamControl::StreamControlState
CBaseStreamControl::CheckStreamState( IMediaSample
* pSample
)
278 REFERENCE_TIME rtBufferStart
, rtBufferStop
;
279 const BOOL bNoBufferTimes
=
281 FAILED(pSample
->GetTime(&rtBufferStart
, &rtBufferStop
));
283 StreamControlState state
;
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
;
297 state
= CheckSampleTimes( &rtBufferStart
, &rtBufferStop
);
298 if (state
== STREAM_FLOWING
)
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"),
310 state
= STREAM_FLOWING
;
315 // We're in discarding mode
317 // If we've no clock, discard as fast as we can
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
) {
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
);
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
)
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
) {
364 } else if (m_tStopTime
!= MAX_TIME
&& m_tStartTime
== MAX_TIME
) {
366 } else if (m_tStopTime
!= MAX_TIME
&& m_tStartTime
!= MAX_TIME
) {
367 if (m_tStartTime
<= m_tStopTime
) {
375 // always start off flowing when the graph starts streaming
376 // unless told otherwise
377 m_StreamState
= STREAM_FLOWING
;
378 m_FilterState
= new_state
;
383 DbgLog((LOG_TRACE
,2,TEXT("Filter is RUNNING")));
385 m_tRunStart
= tStart
;
388 default: // case State_Paused:
389 m_FilterState
= new_state
;
396 void CBaseStreamControl::Flushing(BOOL bInProgress
)
398 CAutoLock
lck(&m_CritSec
);
399 m_bIsFlushing
= bInProgress
;