forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
@ -0,0 +1,402 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
Reference in New Issue
Block a user