123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- //------------------------------------------------------------------------------
- // File: StrmCtl.cpp
- //
- // Desc: DirectShow base classes.
- //
- // Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------------------------
- #include <streams.h>
- #include <strmctl.h>
- CBaseStreamControl::CBaseStreamControl(__inout HRESULT *phr)
- : m_StreamState(STREAM_FLOWING)
- , m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop
- , m_tStartTime(MAX_TIME)
- , m_tStopTime(MAX_TIME)
- , m_StreamEvent(FALSE, phr)
- , m_dwStartCookie(0)
- , m_dwStopCookie(0)
- , m_pRefClock(NULL)
- , m_FilterState(State_Stopped)
- , m_bIsFlushing(FALSE)
- , m_bStopSendExtra(FALSE)
- {}
- CBaseStreamControl::~CBaseStreamControl()
- {
- // Make sure we release the clock.
- SetSyncSource(NULL);
- return;
- }
- STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie)
- {
- CAutoLock lck(&m_CritSec);
- m_bStopSendExtra = FALSE; // reset
- m_bStopExtraSent = FALSE;
- if (ptStop)
- {
- if (*ptStop == MAX_TIME)
- {
- DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));
- CancelStop();
- // If there's now a command to start in the future, we assume
- // they want to be stopped when the graph is first run
- if (m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {
- m_StreamState = STREAM_DISCARDING;
- DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
- }
- return NOERROR;
- }
- DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),
- (int)(*ptStop/10000), bSendExtra));
- // if the first command is to stop in the future, then we assume they
- // want to be started when the graph is first run
- if (m_FilterState == State_Stopped && m_tStartTime > *ptStop) {
- m_StreamState = STREAM_FLOWING;
- DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
- }
- m_bStopSendExtra = bSendExtra;
- m_tStopTime = *ptStop;
- m_dwStopCookie = dwCookie;
- m_StreamStateOnStop = STREAM_DISCARDING;
- }
- else
- {
- DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));
- // sending an extra frame when told to stop now would mess people up
- m_bStopSendExtra = FALSE;
- m_tStopTime = MAX_TIME;
- m_dwStopCookie = 0;
- m_StreamState = STREAM_DISCARDING;
- m_StreamStateOnStop = STREAM_FLOWING; // no pending stop
- }
- // we might change our mind what to do with a sample we're blocking
- m_StreamEvent.Set();
- return NOERROR;
- }
- STDMETHODIMP CBaseStreamControl::StartAt
- ( const REFERENCE_TIME *ptStart, DWORD dwCookie )
- {
- CAutoLock lck(&m_CritSec);
- if (ptStart)
- {
- if (*ptStart == MAX_TIME)
- {
- DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));
- CancelStart();
- // If there's now a command to stop in the future, we assume
- // they want to be started when the graph is first run
- if (m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {
- DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
- m_StreamState = STREAM_FLOWING;
- }
- return NOERROR;
- }
- DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));
- // if the first command is to start in the future, then we assume they
- // want to be stopped when the graph is first run
- if (m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {
- DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
- m_StreamState = STREAM_DISCARDING;
- }
- m_tStartTime = *ptStart;
- m_dwStartCookie = dwCookie;
- // if (m_tStopTime == m_tStartTime) CancelStop();
- }
- else
- {
- DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));
- m_tStartTime = MAX_TIME;
- m_dwStartCookie = 0;
- m_StreamState = STREAM_FLOWING;
- }
- // we might change our mind what to do with a sample we're blocking
- m_StreamEvent.Set();
- return NOERROR;
- }
- // Retrieve information about current settings
- STDMETHODIMP CBaseStreamControl::GetInfo(__out AM_STREAM_INFO *pInfo)
- {
- if (pInfo == NULL)
- return E_POINTER;
- pInfo->tStart = m_tStartTime;
- pInfo->tStop = m_tStopTime;
- pInfo->dwStartCookie = m_dwStartCookie;
- pInfo->dwStopCookie = m_dwStopCookie;
- pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;
- pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;
- pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;
- switch (m_StreamState) {
- default:
- DbgBreak("Invalid stream state");
- case STREAM_FLOWING:
- break;
- case STREAM_DISCARDING:
- pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;
- break;
- }
- return S_OK;
- }
- void CBaseStreamControl::ExecuteStop()
- {
- ASSERT(CritCheckIn(&m_CritSec));
- m_StreamState = m_StreamStateOnStop;
- if (m_dwStopCookie && m_pSink) {
- DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),
- m_dwStopCookie));
- m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);
- }
- CancelStop(); // This will do the tidy up
- }
- void CBaseStreamControl::ExecuteStart()
- {
- ASSERT(CritCheckIn(&m_CritSec));
- m_StreamState = STREAM_FLOWING;
- if (m_dwStartCookie) {
- DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),
- m_dwStartCookie));
- m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);
- }
- CancelStart(); // This will do the tidy up
- }
- void CBaseStreamControl::CancelStop()
- {
- ASSERT(CritCheckIn(&m_CritSec));
- m_tStopTime = MAX_TIME;
- m_dwStopCookie = 0;
- m_StreamStateOnStop = STREAM_FLOWING;
- }
- void CBaseStreamControl::CancelStart()
- {
- ASSERT(CritCheckIn(&m_CritSec));
- m_tStartTime = MAX_TIME;
- m_dwStartCookie = 0;
- }
- // This guy will return one of the three StreamControlState's. Here's what the caller
- // should do for each one:
- //
- // STREAM_FLOWING: Proceed as usual (render or pass the sample on)
- // STREAM_DISCARDING: Calculate the time 'til *pSampleStart and wait that long
- // for the event handle (GetStreamEventHandle()). If the
- // wait expires, throw the sample away. If the event
- // fires, call me back, I've changed my mind.
- // I use pSampleStart (not Stop) so that live sources don't
- // block for the duration of their samples, since the clock
- // will always read approximately pSampleStart when called
- // All through this code, you'll notice the following rules:
- // - When start and stop time are the same, it's as if start was first
- // - An event is considered inside the sample when it's >= sample start time
- // but < sample stop time
- // - if any part of the sample is supposed to be sent, we'll send the whole
- // thing since we don't break it into smaller pieces
- // - If we skip over a start or stop without doing it, we still signal the event
- // and reset ourselves in case somebody's waiting for the event, and to make
- // sure we notice that the event is past and should be forgotten
- // Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):
- //
- // 1. xo<--> start then stop
- // 2. ox<--> stop then start
- // 3. x<o-> start
- // 4. o<x-> stop then start
- // 5. x<-->o start
- // 6. o<-->x stop
- // 7. <x->o start
- // 8. <o->x no change
- // 9. <xo> start
- // 10. <ox> stop then start
- // 11. <-->xo no change
- // 12. <-->ox no change
- // 13. x<--> start
- // 14. <x-> start
- // 15. <-->x no change
- // 16. o<--> stop
- // 17. <o-> no change
- // 18. <-->o no change
- // 19. <--> no change
- enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes
- ( __in const REFERENCE_TIME * pSampleStart, __in const REFERENCE_TIME * pSampleStop )
- {
- CAutoLock lck(&m_CritSec);
- ASSERT(!m_bIsFlushing);
- ASSERT(pSampleStart && pSampleStop);
- // Don't ask me how I came up with the code below to handle all 19 cases
- // - DannyMi
- if (m_tStopTime >= *pSampleStart)
- {
- if (m_tStartTime >= *pSampleStop)
- return m_StreamState; // cases 8 11 12 15 17 18 19
- if (m_tStopTime < m_tStartTime)
- ExecuteStop(); // case 10
- ExecuteStart(); // cases 3 5 7 9 13 14
- return m_StreamState;
- }
- if (m_tStartTime >= *pSampleStop)
- {
- ExecuteStop(); // cases 6 16
- return m_StreamState;
- }
- if (m_tStartTime <= m_tStopTime)
- {
- ExecuteStart();
- ExecuteStop();
- return m_StreamState; // case 1
- }
- else
- {
- ExecuteStop();
- ExecuteStart();
- return m_StreamState; // cases 2 4
- }
- }
- enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample )
- {
- REFERENCE_TIME rtBufferStart, rtBufferStop;
- const BOOL bNoBufferTimes =
- pSample == NULL ||
- FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));
- StreamControlState state;
- LONG lWait;
- do
- {
- // something has to break out of the blocking
- if (m_bIsFlushing || m_FilterState == State_Stopped)
- return STREAM_DISCARDING;
- if (bNoBufferTimes) {
- // Can't do anything until we get a time stamp
- state = m_StreamState;
- break;
- } else {
- state = CheckSampleTimes( &rtBufferStart, &rtBufferStop );
- if (state == STREAM_FLOWING)
- break;
- // we aren't supposed to send this, but we've been
- // told to send one more than we were supposed to
- // (and the stop isn't still pending and we're streaming)
- if (m_bStopSendExtra && !m_bStopExtraSent &&
- m_tStopTime == MAX_TIME &&
- m_FilterState != State_Stopped) {
- m_bStopExtraSent = TRUE;
- DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),
- m_dwStopCookie));
- state = STREAM_FLOWING;
- break;
- }
- }
- // We're in discarding mode
- // If we've no clock, discard as fast as we can
- if (!m_pRefClock) {
- break;
- // If we're paused, we can't discard in a timely manner because
- // there's no such thing as stream times. We must block until
- // we run or stop, or we'll end up throwing the whole stream away
- // as quickly as possible
- } else if (m_FilterState == State_Paused) {
- lWait = INFINITE;
- } else {
- // wait until it's time for the sample until we say "discard"
- // ("discard in a timely fashion")
- REFERENCE_TIME rtNow;
- EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));
- rtNow -= m_tRunStart; // Into relative ref-time
- lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms
- if (lWait < 10) break; // Not worth waiting - discard early
- }
- } while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);
- return state;
- }
- void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart )
- {
- CAutoLock lck(&m_CritSec);
- // or we will get confused
- if (m_FilterState == new_state)
- return;
- switch (new_state)
- {
- case State_Stopped:
- DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));
- // execute any pending starts and stops in the right order,
- // to make sure all notifications get sent, and we end up
- // in the right state to begin next time (??? why not?)
- if (m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {
- ExecuteStart();
- } else if (m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {
- ExecuteStop();
- } else if (m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {
- if (m_tStartTime <= m_tStopTime) {
- ExecuteStart();
- ExecuteStop();
- } else {
- ExecuteStop();
- ExecuteStart();
- }
- }
- // always start off flowing when the graph starts streaming
- // unless told otherwise
- m_StreamState = STREAM_FLOWING;
- m_FilterState = new_state;
- break;
- case State_Running:
- DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));
- m_tRunStart = tStart;
- // fall-through
- default: // case State_Paused:
- m_FilterState = new_state;
- }
- // unblock!
- m_StreamEvent.Set();
- }
- void CBaseStreamControl::Flushing(BOOL bInProgress)
- {
- CAutoLock lck(&m_CritSec);
- m_bIsFlushing = bInProgress;
- m_StreamEvent.Set();
- }
|