Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
12 KiB

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