//------------------------------------------------------------------------------

// File: StrmCtl.h

//

// Desc: DirectShow base classes.

//

// Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.

//------------------------------------------------------------------------------





#ifndef __strmctl_h__

#define __strmctl_h__



class CBaseStreamControl : public IAMStreamControl

{

public:

    // Used by the implementation

    enum StreamControlState

    { STREAM_FLOWING = 0x1000,

      STREAM_DISCARDING

    };



private:

    enum StreamControlState m_StreamState;      // Current stream state

    enum StreamControlState m_StreamStateOnStop;    // State after next stop

                        // (i.e.Blocking or Discarding)



    REFERENCE_TIME  m_tStartTime;       // MAX_TIME implies none

    REFERENCE_TIME  m_tStopTime;        // MAX_TIME implies none

    DWORD           m_dwStartCookie;    // Cookie for notification to app

    DWORD           m_dwStopCookie;     // Cookie for notification to app

    volatile BOOL   m_bIsFlushing;      // No optimization pls!

    volatile BOOL   m_bStopSendExtra;   // bSendExtra was set

    volatile BOOL   m_bStopExtraSent;   // the extra one was sent



    CCritSec        m_CritSec;          // CritSec to guard above attributes



    // Event to fire when we can come

    // out of blocking, or to come out of waiting

    // to discard if we change our minds.

    //

    CAMEvent            m_StreamEvent;



    // All of these methods execute immediately.  Helpers for others.

    //

    void ExecuteStop();

    void ExecuteStart();

    void CancelStop();

    void CancelStart();



    // Some things we need to be told by our owning filter

    // Your pin must also expose IAMStreamControl when QI'd for it!

    //

    IReferenceClock *   m_pRefClock;        // Need it to set advises

                        // Filter must tell us via

                        // SetSyncSource

    IMediaEventSink *   m_pSink;            // Event sink

                        // Filter must tell us after it

                        // creates it in JoinFilterGraph()

    FILTER_STATE    m_FilterState;          // Just need it!

                        // Filter must tell us via

                        // NotifyFilterState

    REFERENCE_TIME  m_tRunStart;            // Per the Run call to the filter



    // 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 *pSampleStop 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.

    //

    enum StreamControlState CheckSampleTimes( const REFERENCE_TIME * pSampleStart,

                          const REFERENCE_TIME * pSampleStop );



public:

    // You don't have to tell us much when we're created, but there are other

    // obligations that must be met.  See SetSyncSource & NotifyFilterState

    // below.

    //

    CBaseStreamControl();

    ~CBaseStreamControl();



    // If you want this class to work properly, there are thing you need to

    // (keep) telling it.  Filters with pins that use this class

    // should ensure that they pass through to this method any calls they

    // receive on their SetSyncSource.



    // We need a clock to see what time it is.  This is for the

    // "discard in a timely fashion" logic.  If we discard everything as

    // quick as possible, a whole 60 minute file could get discarded in the

    // first 10 seconds, and if somebody wants to turn streaming on at 30 

    // minutes into the file, and they make the call more than a few seconds

    // after the graph is run, it may be too late!

    // So we hold every sample until it's time has gone, then we discard it.

    // The filter should call this when it gets a SetSyncSource

    //

    void SetSyncSource( IReferenceClock * pRefClock )

    {

        CAutoLock lck(&m_CritSec);

        if (m_pRefClock) m_pRefClock->Release();

            m_pRefClock = pRefClock;

        if (m_pRefClock) 

            m_pRefClock->AddRef();

    }



    // Set event sink for notifications

    // The filter should call this in its JoinFilterGraph after it creates the

    // IMediaEventSink

    //

    void SetFilterGraph( IMediaEventSink *pSink ) {

        m_pSink = pSink;

    }



    // Since we schedule in stream time, we need the tStart and must track the

    // state of our owning filter.

    // The app should call this ever state change

    //

    void NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart = 0 );



    // Filter should call Flushing(TRUE) in BeginFlush,

    // and Flushing(FALSE) in EndFlush.

    //

    void Flushing( BOOL bInProgress );





    // The two main methods of IAMStreamControl



    // Class adds default values suitable for immediate

    // muting and unmuting of the stream.



    STDMETHODIMP StopAt( const REFERENCE_TIME * ptStop = NULL,

             BOOL bSendExtra = FALSE,

             DWORD dwCookie = 0 );

    STDMETHODIMP StartAt( const REFERENCE_TIME * ptStart = NULL,

                  DWORD dwCookie = 0 );

    STDMETHODIMP GetInfo( AM_STREAM_INFO *pInfo);



    // Helper function for pin's receive method.  Call this with

    // the sample and we'll tell you what to do with it.  We'll do a

    // WaitForSingleObject within this call if one is required.  This is

    // a "What should I do with this sample?" kind of call. We'll tell the

    // caller to either flow it or discard it.

    // If pSample is NULL we evaluate based on the current state

    // settings

    enum StreamControlState CheckStreamState( IMediaSample * pSample );



private:

    // These don't require locking, but we are relying on the fact that

    // m_StreamState can be retrieved with integrity, and is a snap shot that

    // may have just been, or may be just about to be, changed.

    HANDLE GetStreamEventHandle() const { return m_StreamEvent; }

    enum StreamControlState GetStreamState() const { return m_StreamState; }

    BOOL IsStreaming() const { return m_StreamState == STREAM_FLOWING; }

};



#endif

