1
0

strmctl.cpp 12 KB

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