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