forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			403 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			403 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | //------------------------------------------------------------------------------
 | ||
|  | // 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(); | ||
|  | } |