| //------------------------------------------------------------------------------ | |
| // File: AMFilter.h | |
| // | |
| // Desc: DirectShow base classes - efines class hierarchy for streams | |
| // architecture. | |
| // | |
| // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. | |
| //------------------------------------------------------------------------------ | |
| #ifndef __FILTER__ | |
| #define __FILTER__ | |
| /* The following classes are declared in this header: */ | |
| class CBaseMediaFilter; // IMediaFilter support | |
| class CBaseFilter; // IBaseFilter,IMediaFilter support | |
| class CBasePin; // Abstract base class for IPin interface | |
| class CEnumPins; // Enumerate input and output pins | |
| class CEnumMediaTypes; // Enumerate the pin's preferred formats | |
| class CBaseOutputPin; // Adds data provider member functions | |
| class CBaseInputPin; // Implements IMemInputPin interface | |
| class CMediaSample; // Basic transport unit for IMemInputPin | |
| class CBaseAllocator; // General list guff for most allocators | |
| class CMemAllocator; // Implements memory buffer allocation | |
| //===================================================================== | |
| //===================================================================== | |
| // | |
| // QueryFilterInfo and QueryPinInfo AddRef the interface pointers | |
| // they return. You can use the macro below to release the interface. | |
| // | |
| //===================================================================== | |
| //===================================================================== | |
| #define QueryFilterInfoReleaseGraph(fi) if ((fi).pGraph) (fi).pGraph->Release(); | |
| #define QueryPinInfoReleaseFilter(pi) if ((pi).pFilter) (pi).pFilter->Release(); | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CBaseMediaFilter | |
| // | |
| // Abstract base class implementing IMediaFilter. | |
| // | |
| // Typically you will derive your filter from CBaseFilter rather than | |
| // this, unless you are implementing an object such as a plug-in | |
| // distributor that needs to support IMediaFilter but not IBaseFilter. | |
| // | |
| // Note that IMediaFilter is derived from IPersist to allow query of | |
| // class id. | |
| //===================================================================== | |
| //===================================================================== | |
| class AM_NOVTABLE CBaseMediaFilter : public CUnknown, | |
| public IMediaFilter | |
| { | |
| protected: | |
| FILTER_STATE m_State; // current state: running, paused | |
| IReferenceClock *m_pClock; // this filter's reference clock | |
| // note: all filters in a filter graph use the same clock | |
| // offset from stream time to reference time | |
| CRefTime m_tStart; | |
| CLSID m_clsid; // This filters clsid | |
| // used for serialization | |
| CCritSec *m_pLock; // Object we use for locking | |
| public: | |
| CBaseMediaFilter( | |
| __in_opt LPCTSTR pName, | |
| __inout_opt LPUNKNOWN pUnk, | |
| __in CCritSec *pLock, | |
| REFCLSID clsid); | |
| virtual ~CBaseMediaFilter(); | |
| DECLARE_IUNKNOWN | |
| // override this to say what interfaces we support where | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); | |
| // | |
| // --- IPersist method --- | |
| // | |
| STDMETHODIMP GetClassID(__out CLSID *pClsID); | |
| // --- IMediaFilter methods --- | |
| STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State); | |
| STDMETHODIMP SetSyncSource(__inout_opt IReferenceClock *pClock); | |
| STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock); | |
| // default implementation of Stop and Pause just record the | |
| // state. Override to activate or de-activate your filter. | |
| // Note that Run when called from Stopped state will call Pause | |
| // to ensure activation, so if you are a source or transform | |
| // you will probably not need to override Run. | |
| STDMETHODIMP Stop(); | |
| STDMETHODIMP Pause(); | |
| // the start parameter is the difference to be added to the | |
| // sample's stream time to get the reference time for | |
| // its presentation | |
| STDMETHODIMP Run(REFERENCE_TIME tStart); | |
| // --- helper methods --- | |
| // return the current stream time - ie find out what | |
| // stream time should be appearing now | |
| virtual HRESULT StreamTime(CRefTime& rtStream); | |
| // Is the filter currently active? (running or paused) | |
| BOOL IsActive() { | |
| CAutoLock cObjectLock(m_pLock); | |
| return ((m_State == State_Paused) || (m_State == State_Running)); | |
| }; | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CBaseFilter | |
| // | |
| // An abstract class providing basic IBaseFilter support for pin | |
| // enumeration and filter information reading. | |
| // | |
| // We cannot derive from CBaseMediaFilter since methods in IMediaFilter | |
| // are also in IBaseFilter and would be ambiguous. Since much of the code | |
| // assumes that they derive from a class that has m_State and other state | |
| // directly available, we duplicate code from CBaseMediaFilter rather than | |
| // having a member variable. | |
| // | |
| // Derive your filter from this, or from a derived object such as | |
| // CTransformFilter. | |
| //===================================================================== | |
| //===================================================================== | |
| class AM_NOVTABLE CBaseFilter : public CUnknown, // Handles an IUnknown | |
| public IBaseFilter, // The Filter Interface | |
| public IAMovieSetup // For un/registration | |
| { | |
| friend class CBasePin; | |
| protected: | |
| FILTER_STATE m_State; // current state: running, paused | |
| IReferenceClock *m_pClock; // this graph's ref clock | |
| CRefTime m_tStart; // offset from stream time to reference time | |
| CLSID m_clsid; // This filters clsid | |
| // used for serialization | |
| CCritSec *m_pLock; // Object we use for locking | |
| WCHAR *m_pName; // Full filter name | |
| IFilterGraph *m_pGraph; // Graph we belong to | |
| IMediaEventSink *m_pSink; // Called with notify events | |
| LONG m_PinVersion; // Current pin version | |
| public: | |
| CBaseFilter( | |
| __in_opt LPCTSTR pName, // Object description | |
| __inout_opt LPUNKNOWN pUnk, // IUnknown of delegating object | |
| __in CCritSec *pLock, // Object who maintains lock | |
| REFCLSID clsid); // The clsid to be used to serialize this filter | |
| CBaseFilter( | |
| __in_opt LPCTSTR pName, // Object description | |
| __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object | |
| __in CCritSec *pLock, // Object who maintains lock | |
| REFCLSID clsid, // The clsid to be used to serialize this filter | |
| __inout HRESULT *phr); // General OLE return code | |
| #ifdef UNICODE | |
| CBaseFilter( | |
| __in_opt LPCSTR pName, // Object description | |
| __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object | |
| __in CCritSec *pLock, // Object who maintains lock | |
| REFCLSID clsid); // The clsid to be used to serialize this filter | |
| CBaseFilter( | |
| __in_opt LPCSTR pName, // Object description | |
| __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object | |
| __in CCritSec *pLock, // Object who maintains lock | |
| REFCLSID clsid, // The clsid to be used to serialize this filter | |
| __inout HRESULT *phr); // General OLE return code | |
| #endif | |
| ~CBaseFilter(); | |
| DECLARE_IUNKNOWN | |
| // override this to say what interfaces we support where | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); | |
| #ifdef DEBUG | |
| STDMETHODIMP_(ULONG) NonDelegatingRelease(); | |
| #endif | |
| // | |
| // --- IPersist method --- | |
| // | |
| STDMETHODIMP GetClassID(__out CLSID *pClsID); | |
| // --- IMediaFilter methods --- | |
| STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State); | |
| STDMETHODIMP SetSyncSource(__in_opt IReferenceClock *pClock); | |
| STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock); | |
| // override Stop and Pause so we can activate the pins. | |
| // Note that Run will call Pause first if activation needed. | |
| // Override these if you want to activate your filter rather than | |
| // your pins. | |
| STDMETHODIMP Stop(); | |
| STDMETHODIMP Pause(); | |
| // the start parameter is the difference to be added to the | |
| // sample's stream time to get the reference time for | |
| // its presentation | |
| STDMETHODIMP Run(REFERENCE_TIME tStart); | |
| // --- helper methods --- | |
| // return the current stream time - ie find out what | |
| // stream time should be appearing now | |
| virtual HRESULT StreamTime(CRefTime& rtStream); | |
| // Is the filter currently active? | |
| BOOL IsActive() { | |
| CAutoLock cObjectLock(m_pLock); | |
| return ((m_State == State_Paused) || (m_State == State_Running)); | |
| }; | |
| // Is this filter stopped (without locking) | |
| BOOL IsStopped() { | |
| return (m_State == State_Stopped); | |
| }; | |
| // | |
| // --- IBaseFilter methods --- | |
| // | |
| // pin enumerator | |
| STDMETHODIMP EnumPins( | |
| __deref_out IEnumPins ** ppEnum); | |
| // default behaviour of FindPin assumes pin ids are their names | |
| STDMETHODIMP FindPin( | |
| LPCWSTR Id, | |
| __deref_out IPin ** ppPin | |
| ); | |
| STDMETHODIMP QueryFilterInfo( | |
| __out FILTER_INFO * pInfo); | |
| STDMETHODIMP JoinFilterGraph( | |
| __inout_opt IFilterGraph * pGraph, | |
| __in_opt LPCWSTR pName); | |
| // return a Vendor information string. Optional - may return E_NOTIMPL. | |
| // memory returned should be freed using CoTaskMemFree | |
| // default implementation returns E_NOTIMPL | |
| STDMETHODIMP QueryVendorInfo( | |
| __deref_out LPWSTR* pVendorInfo | |
| ); | |
| // --- helper methods --- | |
| // send an event notification to the filter graph if we know about it. | |
| // returns S_OK if delivered, S_FALSE if the filter graph does not sink | |
| // events, or an error otherwise. | |
| HRESULT NotifyEvent( | |
| long EventCode, | |
| LONG_PTR EventParam1, | |
| LONG_PTR EventParam2); | |
| // return the filter graph we belong to | |
| __out_opt IFilterGraph *GetFilterGraph() { | |
| return m_pGraph; | |
| } | |
| // Request reconnect | |
| // pPin is the pin to reconnect | |
| // pmt is the type to reconnect with - can be NULL | |
| // Calls ReconnectEx on the filter graph | |
| HRESULT ReconnectPin(IPin *pPin, __in_opt AM_MEDIA_TYPE const *pmt); | |
| // find out the current pin version (used by enumerators) | |
| virtual LONG GetPinVersion(); | |
| void IncrementPinVersion(); | |
| // you need to supply these to access the pins from the enumerator | |
| // and for default Stop and Pause/Run activation. | |
| virtual int GetPinCount() PURE; | |
| virtual CBasePin *GetPin(int n) PURE; | |
| // --- IAMovieSetup methods --- | |
| STDMETHODIMP Register(); // ask filter to register itself | |
| STDMETHODIMP Unregister(); // and unregister itself | |
| // --- setup helper methods --- | |
| // (override to return filters setup data) | |
| virtual __out_opt LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; } | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CBasePin | |
| // | |
| // Abstract class that supports the basics of IPin | |
| //===================================================================== | |
| //===================================================================== | |
| class AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl | |
| { | |
| protected: | |
| WCHAR * m_pName; // This pin's name | |
| IPin *m_Connected; // Pin we have connected to | |
| PIN_DIRECTION m_dir; // Direction of this pin | |
| CCritSec *m_pLock; // Object we use for locking | |
| bool m_bRunTimeError; // Run time error generated | |
| bool m_bCanReconnectWhenActive; // OK to reconnect when active | |
| bool m_bTryMyTypesFirst; // When connecting enumerate | |
| // this pin's types first | |
| CBaseFilter *m_pFilter; // Filter we were created by | |
| IQualityControl *m_pQSink; // Target for Quality messages | |
| LONG m_TypeVersion; // Holds current type version | |
| CMediaType m_mt; // Media type of connection | |
| CRefTime m_tStart; // time from NewSegment call | |
| CRefTime m_tStop; // time from NewSegment | |
| double m_dRate; // rate from NewSegment | |
| #ifdef DEBUG | |
| LONG m_cRef; // Ref count tracing | |
| #endif | |
| // displays pin connection information | |
| #ifdef DEBUG | |
| void DisplayPinInfo(IPin *pReceivePin); | |
| void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt); | |
| #else | |
| void DisplayPinInfo(IPin *pReceivePin) {}; | |
| void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {}; | |
| #endif | |
| // used to agree a media type for a pin connection | |
| // given a specific media type, attempt a connection (includes | |
| // checking that the type is acceptable to this pin) | |
| HRESULT | |
| AttemptConnection( | |
| IPin* pReceivePin, // connect to this pin | |
| const CMediaType* pmt // using this type | |
| ); | |
| // try all the media types in this enumerator - for each that | |
| // we accept, try to connect using ReceiveConnection. | |
| HRESULT TryMediaTypes( | |
| IPin *pReceivePin, // connect to this pin | |
| __in_opt const CMediaType *pmt, // proposed type from Connect | |
| IEnumMediaTypes *pEnum); // try this enumerator | |
| // establish a connection with a suitable mediatype. Needs to | |
| // propose a media type if the pmt pointer is null or partially | |
| // specified - use TryMediaTypes on both our and then the other pin's | |
| // enumerator until we find one that works. | |
| HRESULT AgreeMediaType( | |
| IPin *pReceivePin, // connect to this pin | |
| const CMediaType *pmt); // proposed type from Connect | |
| public: | |
| CBasePin( | |
| __in_opt LPCTSTR pObjectName, // Object description | |
| __in CBaseFilter *pFilter, // Owning filter who knows about pins | |
| __in CCritSec *pLock, // Object who implements the lock | |
| __inout HRESULT *phr, // General OLE return code | |
| __in_opt LPCWSTR pName, // Pin name for us | |
| PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT | |
| #ifdef UNICODE | |
| CBasePin( | |
| __in_opt LPCSTR pObjectName, // Object description | |
| __in CBaseFilter *pFilter, // Owning filter who knows about pins | |
| __in CCritSec *pLock, // Object who implements the lock | |
| __inout HRESULT *phr, // General OLE return code | |
| __in_opt LPCWSTR pName, // Pin name for us | |
| PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT | |
| #endif | |
| virtual ~CBasePin(); | |
| DECLARE_IUNKNOWN | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); | |
| STDMETHODIMP_(ULONG) NonDelegatingRelease(); | |
| STDMETHODIMP_(ULONG) NonDelegatingAddRef(); | |
| // --- IPin methods --- | |
| // take lead role in establishing a connection. Media type pointer | |
| // may be null, or may point to partially-specified mediatype | |
| // (subtype or format type may be GUID_NULL). | |
| STDMETHODIMP Connect( | |
| IPin * pReceivePin, | |
| __in_opt const AM_MEDIA_TYPE *pmt // optional media type | |
| ); | |
| // (passive) accept a connection from another pin | |
| STDMETHODIMP ReceiveConnection( | |
| IPin * pConnector, // this is the initiating connecting pin | |
| const AM_MEDIA_TYPE *pmt // this is the media type we will exchange | |
| ); | |
| STDMETHODIMP Disconnect(); | |
| STDMETHODIMP ConnectedTo(__deref_out IPin **pPin); | |
| STDMETHODIMP ConnectionMediaType(__out AM_MEDIA_TYPE *pmt); | |
| STDMETHODIMP QueryPinInfo( | |
| __out PIN_INFO * pInfo | |
| ); | |
| STDMETHODIMP QueryDirection( | |
| __out PIN_DIRECTION * pPinDir | |
| ); | |
| STDMETHODIMP QueryId( | |
| __deref_out LPWSTR * Id | |
| ); | |
| // does the pin support this media type | |
| STDMETHODIMP QueryAccept( | |
| const AM_MEDIA_TYPE *pmt | |
| ); | |
| // return an enumerator for this pins preferred media types | |
| STDMETHODIMP EnumMediaTypes( | |
| __deref_out IEnumMediaTypes **ppEnum | |
| ); | |
| // return an array of IPin* - the pins that this pin internally connects to | |
| // All pins put in the array must be AddReffed (but no others) | |
| // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE | |
| // Default: return E_NOTIMPL | |
| // The filter graph will interpret NOT_IMPL as any input pin connects to | |
| // all visible output pins and vice versa. | |
| // apPin can be NULL if nPin==0 (not otherwise). | |
| STDMETHODIMP QueryInternalConnections( | |
| __out_ecount_part(*nPin,*nPin) IPin* *apPin, // array of IPin* | |
| __inout ULONG *nPin // on input, the number of slots | |
| // on output the number of pins | |
| ) { return E_NOTIMPL; } | |
| // Called when no more data will be sent | |
| STDMETHODIMP EndOfStream(void); | |
| // Begin/EndFlush still PURE | |
| // NewSegment notifies of the start/stop/rate applying to the data | |
| // about to be received. Default implementation records data and | |
| // returns S_OK. | |
| // Override this to pass downstream. | |
| STDMETHODIMP NewSegment( | |
| REFERENCE_TIME tStart, | |
| REFERENCE_TIME tStop, | |
| double dRate); | |
| //================================================================================ | |
| // IQualityControl methods | |
| //================================================================================ | |
| STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); | |
| STDMETHODIMP SetSink(IQualityControl * piqc); | |
| // --- helper methods --- | |
| // Returns true if the pin is connected. false otherwise. | |
| BOOL IsConnected(void) {return (m_Connected != NULL); }; | |
| // Return the pin this is connected to (if any) | |
| IPin * GetConnected() { return m_Connected; }; | |
| // Check if our filter is currently stopped | |
| BOOL IsStopped() { | |
| return (m_pFilter->m_State == State_Stopped); | |
| }; | |
| // find out the current type version (used by enumerators) | |
| virtual LONG GetMediaTypeVersion(); | |
| void IncrementTypeVersion(); | |
| // switch the pin to active (paused or running) mode | |
| // not an error to call this if already active | |
| virtual HRESULT Active(void); | |
| // switch the pin to inactive state - may already be inactive | |
| virtual HRESULT Inactive(void); | |
| // Notify of Run() from filter | |
| virtual HRESULT Run(REFERENCE_TIME tStart); | |
| // check if the pin can support this specific proposed type and format | |
| virtual HRESULT CheckMediaType(const CMediaType *) PURE; | |
| // set the connection to use this format (previously agreed) | |
| virtual HRESULT SetMediaType(const CMediaType *); | |
| // check that the connection is ok before verifying it | |
| // can be overridden eg to check what interfaces will be supported. | |
| virtual HRESULT CheckConnect(IPin *); | |
| // Set and release resources required for a connection | |
| virtual HRESULT BreakConnect(); | |
| virtual HRESULT CompleteConnect(IPin *pReceivePin); | |
| // returns the preferred formats for a pin | |
| virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType); | |
| // access to NewSegment values | |
| REFERENCE_TIME CurrentStopTime() { | |
| return m_tStop; | |
| } | |
| REFERENCE_TIME CurrentStartTime() { | |
| return m_tStart; | |
| } | |
| double CurrentRate() { | |
| return m_dRate; | |
| } | |
| // Access name | |
| LPWSTR Name() { return m_pName; }; | |
| // Can reconnectwhen active? | |
| void SetReconnectWhenActive(bool bCanReconnect) | |
| { | |
| m_bCanReconnectWhenActive = bCanReconnect; | |
| } | |
| bool CanReconnectWhenActive() | |
| { | |
| return m_bCanReconnectWhenActive; | |
| } | |
| protected: | |
| STDMETHODIMP DisconnectInternal(); | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CEnumPins | |
| // | |
| // Pin enumerator class that works by calling CBaseFilter. This interface | |
| // is provided by CBaseFilter::EnumPins and calls GetPinCount() and | |
| // GetPin() to enumerate existing pins. Needs to be a separate object so | |
| // that it can be cloned (creating an existing object at the same | |
| // position in the enumeration) | |
| // | |
| //===================================================================== | |
| //===================================================================== | |
| class CEnumPins : public IEnumPins // The interface we support | |
| { | |
| int m_Position; // Current ordinal position | |
| int m_PinCount; // Number of pins available | |
| CBaseFilter *m_pFilter; // The filter who owns us | |
| LONG m_Version; // Pin version information | |
| LONG m_cRef; | |
| typedef CGenericList<CBasePin> CPinList; | |
| CPinList m_PinCache; // These pointers have not been AddRef'ed and | |
| // so they should not be dereferenced. They are | |
| // merely kept to ID which pins have been enumerated. | |
| #ifdef DEBUG | |
| DWORD m_dwCookie; | |
| #endif | |
| /* If while we are retrieving a pin for example from the filter an error | |
| occurs we assume that our internal state is stale with respect to the | |
| filter (someone may have deleted all the pins). We can check before | |
| starting whether or not the operation is likely to fail by asking the | |
| filter what it's current version number is. If the filter has not | |
| overriden the GetPinVersion method then this will always match */ | |
| BOOL AreWeOutOfSync() { | |
| return (m_pFilter->GetPinVersion() == m_Version ? FALSE : TRUE); | |
| }; | |
| /* This method performs the same operations as Reset, except is does not clear | |
| the cache of pins already enumerated. */ | |
| STDMETHODIMP Refresh(); | |
| public: | |
| CEnumPins( | |
| __in CBaseFilter *pFilter, | |
| __in_opt CEnumPins *pEnumPins); | |
| virtual ~CEnumPins(); | |
| // IUnknown | |
| STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv); | |
| STDMETHODIMP_(ULONG) AddRef(); | |
| STDMETHODIMP_(ULONG) Release(); | |
| // IEnumPins | |
| STDMETHODIMP Next( | |
| ULONG cPins, // place this many pins... | |
| __out_ecount(cPins) IPin ** ppPins, // ...in this array of IPin* | |
| __out_opt ULONG * pcFetched // actual count passed returned here | |
| ); | |
| STDMETHODIMP Skip(ULONG cPins); | |
| STDMETHODIMP Reset(); | |
| STDMETHODIMP Clone(__deref_out IEnumPins **ppEnum); | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CEnumMediaTypes | |
| // | |
| // Enumerates the preferred formats for input and output pins | |
| //===================================================================== | |
| //===================================================================== | |
| class CEnumMediaTypes : public IEnumMediaTypes // The interface we support | |
| { | |
| int m_Position; // Current ordinal position | |
| CBasePin *m_pPin; // The pin who owns us | |
| LONG m_Version; // Media type version value | |
| LONG m_cRef; | |
| #ifdef DEBUG | |
| DWORD m_dwCookie; | |
| #endif | |
| /* The media types a filter supports can be quite dynamic so we add to | |
| the general IEnumXXXX interface the ability to be signaled when they | |
| change via an event handle the connected filter supplies. Until the | |
| Reset method is called after the state changes all further calls to | |
| the enumerator (except Reset) will return E_UNEXPECTED error code */ | |
| BOOL AreWeOutOfSync() { | |
| return (m_pPin->GetMediaTypeVersion() == m_Version ? FALSE : TRUE); | |
| }; | |
| public: | |
| CEnumMediaTypes( | |
| __in CBasePin *pPin, | |
| __in_opt CEnumMediaTypes *pEnumMediaTypes); | |
| virtual ~CEnumMediaTypes(); | |
| // IUnknown | |
| STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv); | |
| STDMETHODIMP_(ULONG) AddRef(); | |
| STDMETHODIMP_(ULONG) Release(); | |
| // IEnumMediaTypes | |
| STDMETHODIMP Next( | |
| ULONG cMediaTypes, // place this many pins... | |
| __out_ecount(cMediaTypes) AM_MEDIA_TYPE ** ppMediaTypes, // ...in this array | |
| __out_opt ULONG * pcFetched // actual count passed | |
| ); | |
| STDMETHODIMP Skip(ULONG cMediaTypes); | |
| STDMETHODIMP Reset(); | |
| STDMETHODIMP Clone(__deref_out IEnumMediaTypes **ppEnum); | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CBaseOutputPin | |
| // | |
| // class derived from CBasePin that can pass buffers to a connected pin | |
| // that supports IMemInputPin. Supports IPin. | |
| // | |
| // Derive your output pin from this. | |
| // | |
| //===================================================================== | |
| //===================================================================== | |
| class AM_NOVTABLE CBaseOutputPin : public CBasePin | |
| { | |
| protected: | |
| IMemAllocator *m_pAllocator; | |
| IMemInputPin *m_pInputPin; // interface on the downstreaminput pin | |
| // set up in CheckConnect when we connect. | |
| public: | |
| CBaseOutputPin( | |
| __in_opt LPCTSTR pObjectName, | |
| __in CBaseFilter *pFilter, | |
| __in CCritSec *pLock, | |
| __inout HRESULT *phr, | |
| __in_opt LPCWSTR pName); | |
| #ifdef UNICODE | |
| CBaseOutputPin( | |
| __in_opt LPCSTR pObjectName, | |
| __in CBaseFilter *pFilter, | |
| __in CCritSec *pLock, | |
| __inout HRESULT *phr, | |
| __in_opt LPCWSTR pName); | |
| #endif | |
| // override CompleteConnect() so we can negotiate an allocator | |
| virtual HRESULT CompleteConnect(IPin *pReceivePin); | |
| // negotiate the allocator and its buffer size/count and other properties | |
| // Calls DecideBufferSize to set properties | |
| virtual HRESULT DecideAllocator(IMemInputPin * pPin, __deref_out IMemAllocator ** pAlloc); | |
| // override this to set the buffer size and count. Return an error | |
| // if the size/count is not to your liking. | |
| // The allocator properties passed in are those requested by the | |
| // input pin - use eg the alignment and prefix members if you have | |
| // no preference on these. | |
| virtual HRESULT DecideBufferSize( | |
| IMemAllocator * pAlloc, | |
| __inout ALLOCATOR_PROPERTIES * ppropInputRequest | |
| ) PURE; | |
| // returns an empty sample buffer from the allocator | |
| virtual HRESULT GetDeliveryBuffer(__deref_out IMediaSample ** ppSample, | |
| __in_opt REFERENCE_TIME * pStartTime, | |
| __in_opt REFERENCE_TIME * pEndTime, | |
| DWORD dwFlags); | |
| // deliver a filled-in sample to the connected input pin | |
| // note - you need to release it after calling this. The receiving | |
| // pin will addref the sample if it needs to hold it beyond the | |
| // call. | |
| virtual HRESULT Deliver(IMediaSample *); | |
| // override this to control the connection | |
| virtual HRESULT InitAllocator(__deref_out IMemAllocator **ppAlloc); | |
| HRESULT CheckConnect(IPin *pPin); | |
| HRESULT BreakConnect(); | |
| // override to call Commit and Decommit | |
| HRESULT Active(void); | |
| HRESULT Inactive(void); | |
| // we have a default handling of EndOfStream which is to return | |
| // an error, since this should be called on input pins only | |
| STDMETHODIMP EndOfStream(void); | |
| // called from elsewhere in our filter to pass EOS downstream to | |
| // our connected input pin | |
| virtual HRESULT DeliverEndOfStream(void); | |
| // same for Begin/EndFlush - we handle Begin/EndFlush since it | |
| // is an error on an output pin, and we have Deliver methods to | |
| // call the methods on the connected pin | |
| STDMETHODIMP BeginFlush(void); | |
| STDMETHODIMP EndFlush(void); | |
| virtual HRESULT DeliverBeginFlush(void); | |
| virtual HRESULT DeliverEndFlush(void); | |
| // deliver NewSegment to connected pin - you will need to | |
| // override this if you queue any data in your output pin. | |
| virtual HRESULT DeliverNewSegment( | |
| REFERENCE_TIME tStart, | |
| REFERENCE_TIME tStop, | |
| double dRate); | |
| //================================================================================ | |
| // IQualityControl methods | |
| //================================================================================ | |
| // All inherited from CBasePin and not overridden here. | |
| // STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); | |
| // STDMETHODIMP SetSink(IQualityControl * piqc); | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CBaseInputPin | |
| // | |
| // derive your standard input pin from this. | |
| // you need to supply GetMediaType and CheckConnect etc (see CBasePin), | |
| // and you need to supply Receive to do something more useful. | |
| // | |
| //===================================================================== | |
| //===================================================================== | |
| class AM_NOVTABLE CBaseInputPin : public CBasePin, | |
| public IMemInputPin | |
| { | |
| protected: | |
| IMemAllocator *m_pAllocator; // Default memory allocator | |
| // allocator is read-only, so received samples | |
| // cannot be modified (probably only relevant to in-place | |
| // transforms | |
| BYTE m_bReadOnly; | |
| // in flushing state (between BeginFlush and EndFlush) | |
| // if TRUE, all Receives are returned with S_FALSE | |
| BYTE m_bFlushing; | |
| // Sample properties - initalized in Receive | |
| AM_SAMPLE2_PROPERTIES m_SampleProps; | |
| public: | |
| CBaseInputPin( | |
| __in_opt LPCTSTR pObjectName, | |
| __in CBaseFilter *pFilter, | |
| __in CCritSec *pLock, | |
| __inout HRESULT *phr, | |
| __in_opt LPCWSTR pName); | |
| #ifdef UNICODE | |
| CBaseInputPin( | |
| __in_opt LPCSTR pObjectName, | |
| __in CBaseFilter *pFilter, | |
| __in CCritSec *pLock, | |
| __inout HRESULT *phr, | |
| __in_opt LPCWSTR pName); | |
| #endif | |
| virtual ~CBaseInputPin(); | |
| DECLARE_IUNKNOWN | |
| // override this to publicise our interfaces | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); | |
| // return the allocator interface that this input pin | |
| // would like the output pin to use | |
| STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator); | |
| // tell the input pin which allocator the output pin is actually | |
| // going to use. | |
| STDMETHODIMP NotifyAllocator( | |
| IMemAllocator * pAllocator, | |
| BOOL bReadOnly); | |
| // do something with this media sample | |
| STDMETHODIMP Receive(IMediaSample *pSample); | |
| // do something with these media samples | |
| STDMETHODIMP ReceiveMultiple ( | |
| __in_ecount(nSamples) IMediaSample **pSamples, | |
| long nSamples, | |
| __out long *nSamplesProcessed); | |
| // See if Receive() blocks | |
| STDMETHODIMP ReceiveCanBlock(); | |
| // Default handling for BeginFlush - call at the beginning | |
| // of your implementation (makes sure that all Receive calls | |
| // fail). After calling this, you need to free any queued data | |
| // and then call downstream. | |
| STDMETHODIMP BeginFlush(void); | |
| // default handling for EndFlush - call at end of your implementation | |
| // - before calling this, ensure that there is no queued data and no thread | |
| // pushing any more without a further receive, then call downstream, | |
| // then call this method to clear the m_bFlushing flag and re-enable | |
| // receives | |
| STDMETHODIMP EndFlush(void); | |
| // this method is optional (can return E_NOTIMPL). | |
| // default implementation returns E_NOTIMPL. Override if you have | |
| // specific alignment or prefix needs, but could use an upstream | |
| // allocator | |
| STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps); | |
| // Release the pin's allocator. | |
| HRESULT BreakConnect(); | |
| // helper method to check the read-only flag | |
| BOOL IsReadOnly() { | |
| return m_bReadOnly; | |
| }; | |
| // helper method to see if we are flushing | |
| BOOL IsFlushing() { | |
| return m_bFlushing; | |
| }; | |
| // Override this for checking whether it's OK to process samples | |
| // Also call this from EndOfStream. | |
| virtual HRESULT CheckStreaming(); | |
| // Pass a Quality notification on to the appropriate sink | |
| HRESULT PassNotify(Quality& q); | |
| //================================================================================ | |
| // IQualityControl methods (from CBasePin) | |
| //================================================================================ | |
| STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); | |
| // no need to override: | |
| // STDMETHODIMP SetSink(IQualityControl * piqc); | |
| // switch the pin to inactive state - may already be inactive | |
| virtual HRESULT Inactive(void); | |
| // Return sample properties pointer | |
| AM_SAMPLE2_PROPERTIES * SampleProps() { | |
| ASSERT(m_SampleProps.cbData != 0); | |
| return &m_SampleProps; | |
| } | |
| }; | |
| /////////////////////////////////////////////////////////////////////////// | |
| // CDynamicOutputPin | |
| // | |
| class CDynamicOutputPin : public CBaseOutputPin, | |
| public IPinFlowControl | |
| { | |
| public: | |
| #ifdef UNICODE | |
| CDynamicOutputPin( | |
| __in_opt LPCSTR pObjectName, | |
| __in CBaseFilter *pFilter, | |
| __in CCritSec *pLock, | |
| __inout HRESULT *phr, | |
| __in_opt LPCWSTR pName); | |
| #endif | |
| CDynamicOutputPin( | |
| __in_opt LPCTSTR pObjectName, | |
| __in CBaseFilter *pFilter, | |
| __in CCritSec *pLock, | |
| __inout HRESULT *phr, | |
| __in_opt LPCWSTR pName); | |
| ~CDynamicOutputPin(); | |
| // IUnknown Methods | |
| DECLARE_IUNKNOWN | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); | |
| // IPin Methods | |
| STDMETHODIMP Disconnect(void); | |
| // IPinFlowControl Methods | |
| STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent); | |
| // Set graph config info | |
| void SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent); | |
| #ifdef DEBUG | |
| virtual HRESULT Deliver(IMediaSample *pSample); | |
| virtual HRESULT DeliverEndOfStream(void); | |
| virtual HRESULT DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate); | |
| #endif // DEBUG | |
| HRESULT DeliverBeginFlush(void); | |
| HRESULT DeliverEndFlush(void); | |
| HRESULT Inactive(void); | |
| HRESULT Active(void); | |
| virtual HRESULT CompleteConnect(IPin *pReceivePin); | |
| virtual HRESULT StartUsingOutputPin(void); | |
| virtual void StopUsingOutputPin(void); | |
| virtual bool StreamingThreadUsingOutputPin(void); | |
| HRESULT ChangeOutputFormat | |
| ( | |
| const AM_MEDIA_TYPE *pmt, | |
| REFERENCE_TIME tSegmentStart, | |
| REFERENCE_TIME tSegmentStop, | |
| double dSegmentRate | |
| ); | |
| HRESULT ChangeMediaType(const CMediaType *pmt); | |
| HRESULT DynamicReconnect(const CMediaType *pmt); | |
| protected: | |
| HRESULT SynchronousBlockOutputPin(void); | |
| HRESULT AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent); | |
| HRESULT UnblockOutputPin(void); | |
| void BlockOutputPin(void); | |
| void ResetBlockState(void); | |
| static HRESULT WaitEvent(HANDLE hEvent); | |
| enum BLOCK_STATE | |
| { | |
| NOT_BLOCKED, | |
| PENDING, | |
| BLOCKED | |
| }; | |
| // This lock should be held when the following class members are | |
| // being used: m_hNotifyCallerPinBlockedEvent, m_BlockState, | |
| // m_dwBlockCallerThreadID and m_dwNumOutstandingOutputPinUsers. | |
| CCritSec m_BlockStateLock; | |
| // This event should be signaled when the output pin is | |
| // not blocked. This is a manual reset event. For more | |
| // information on events, see the documentation for | |
| // CreateEvent() in the Windows SDK. | |
| HANDLE m_hUnblockOutputPinEvent; | |
| // This event will be signaled when block operation succeedes or | |
| // when the user cancels the block operation. The block operation | |
| // can be canceled by calling IPinFlowControl2::Block( 0, NULL ) | |
| // while the block operation is pending. | |
| HANDLE m_hNotifyCallerPinBlockedEvent; | |
| // The state of the current block operation. | |
| BLOCK_STATE m_BlockState; | |
| // The ID of the thread which last called IPinFlowControl::Block(). | |
| // For more information on thread IDs, see the documentation for | |
| // GetCurrentThreadID() in the Windows SDK. | |
| DWORD m_dwBlockCallerThreadID; | |
| // The number of times StartUsingOutputPin() has been sucessfully | |
| // called and a corresponding call to StopUsingOutputPin() has not | |
| // been made. When this variable is greater than 0, the streaming | |
| // thread is calling IPin::NewSegment(), IPin::EndOfStream(), | |
| // IMemInputPin::Receive() or IMemInputPin::ReceiveMultiple(). The | |
| // streaming thread could also be calling: DynamicReconnect(), | |
| // ChangeMediaType() or ChangeOutputFormat(). The output pin cannot | |
| // be blocked while the output pin is being used. | |
| DWORD m_dwNumOutstandingOutputPinUsers; | |
| // This event should be set when the IMediaFilter::Stop() is called. | |
| // This is a manual reset event. It is also set when the output pin | |
| // delivers a flush to the connected input pin. | |
| HANDLE m_hStopEvent; | |
| IGraphConfig* m_pGraphConfig; | |
| // TRUE if the output pin's allocator's samples are read only. | |
| // Otherwise FALSE. For more information, see the documentation | |
| // for IMemInputPin::NotifyAllocator(). | |
| BOOL m_bPinUsesReadOnlyAllocator; | |
| private: | |
| HRESULT Initialize(void); | |
| HRESULT ChangeMediaTypeHelper(const CMediaType *pmt); | |
| #ifdef DEBUG | |
| void AssertValid(void); | |
| #endif // DEBUG | |
| }; | |
| class CAutoUsingOutputPin | |
| { | |
| public: | |
| CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr ); | |
| ~CAutoUsingOutputPin(); | |
| private: | |
| CDynamicOutputPin* m_pOutputPin; | |
| }; | |
| inline CAutoUsingOutputPin::CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr ) : | |
| m_pOutputPin(NULL) | |
| { | |
| // The caller should always pass in valid pointers. | |
| ASSERT( NULL != pOutputPin ); | |
| ASSERT( NULL != phr ); | |
| // Make sure the user initialized phr. | |
| ASSERT( S_OK == *phr ); | |
| HRESULT hr = pOutputPin->StartUsingOutputPin(); | |
| if( FAILED( hr ) ) | |
| { | |
| *phr = hr; | |
| return; | |
| } | |
| m_pOutputPin = pOutputPin; | |
| } | |
| inline CAutoUsingOutputPin::~CAutoUsingOutputPin() | |
| { | |
| if( NULL != m_pOutputPin ) | |
| { | |
| m_pOutputPin->StopUsingOutputPin(); | |
| } | |
| } | |
| #ifdef DEBUG | |
| inline HRESULT CDynamicOutputPin::Deliver(IMediaSample *pSample) | |
| { | |
| // The caller should call StartUsingOutputPin() before calling this | |
| // method. | |
| ASSERT(StreamingThreadUsingOutputPin()); | |
| return CBaseOutputPin::Deliver(pSample); | |
| } | |
| inline HRESULT CDynamicOutputPin::DeliverEndOfStream(void) | |
| { | |
| // The caller should call StartUsingOutputPin() before calling this | |
| // method. | |
| ASSERT( StreamingThreadUsingOutputPin() ); | |
| return CBaseOutputPin::DeliverEndOfStream(); | |
| } | |
| inline HRESULT CDynamicOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) | |
| { | |
| // The caller should call StartUsingOutputPin() before calling this | |
| // method. | |
| ASSERT(StreamingThreadUsingOutputPin()); | |
| return CBaseOutputPin::DeliverNewSegment(tStart, tStop, dRate); | |
| } | |
| #endif // DEBUG | |
| //===================================================================== | |
| //===================================================================== | |
| // Memory allocators | |
| // | |
| // the shared memory transport between pins requires the input pin | |
| // to provide a memory allocator that can provide sample objects. A | |
| // sample object supports the IMediaSample interface. | |
| // | |
| // CBaseAllocator handles the management of free and busy samples. It | |
| // allocates CMediaSample objects. CBaseAllocator is an abstract class: | |
| // in particular it has no method of initializing the list of free | |
| // samples. CMemAllocator is derived from CBaseAllocator and initializes | |
| // the list of samples using memory from the standard IMalloc interface. | |
| // | |
| // If you want your buffers to live in some special area of memory, | |
| // derive your allocator object from CBaseAllocator. If you derive your | |
| // IMemInputPin interface object from CBaseMemInputPin, you will get | |
| // CMemAllocator-based allocation etc for free and will just need to | |
| // supply the Receive handling, and media type / format negotiation. | |
| //===================================================================== | |
| //===================================================================== | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CMediaSample | |
| // | |
| // an object of this class supports IMediaSample and represents a buffer | |
| // for media data with some associated properties. Releasing it returns | |
| // it to a freelist managed by a CBaseAllocator derived object. | |
| //===================================================================== | |
| //===================================================================== | |
| class CMediaSample : public IMediaSample2 // The interface we support | |
| { | |
| protected: | |
| friend class CBaseAllocator; | |
| /* Values for dwFlags - these are used for backward compatiblity | |
| only now - use AM_SAMPLE_xxx | |
| */ | |
| enum { Sample_SyncPoint = 0x01, /* Is this a sync point */ | |
| Sample_Preroll = 0x02, /* Is this a preroll sample */ | |
| Sample_Discontinuity = 0x04, /* Set if start of new segment */ | |
| Sample_TypeChanged = 0x08, /* Has the type changed */ | |
| Sample_TimeValid = 0x10, /* Set if time is valid */ | |
| Sample_MediaTimeValid = 0x20, /* Is the media time valid */ | |
| Sample_TimeDiscontinuity = 0x40, /* Time discontinuity */ | |
| Sample_StopValid = 0x100, /* Stop time valid */ | |
| Sample_ValidFlags = 0x1FF | |
| }; | |
| /* Properties, the media sample class can be a container for a format | |
| change in which case we take a copy of a type through the SetMediaType | |
| interface function and then return it when GetMediaType is called. As | |
| we do no internal processing on it we leave it as a pointer */ | |
| DWORD m_dwFlags; /* Flags for this sample */ | |
| /* Type specific flags are packed | |
| into the top word | |
| */ | |
| DWORD m_dwTypeSpecificFlags; /* Media type specific flags */ | |
| __field_ecount_opt(m_cbBuffer) LPBYTE m_pBuffer; /* Pointer to the complete buffer */ | |
| LONG m_lActual; /* Length of data in this sample */ | |
| LONG m_cbBuffer; /* Size of the buffer */ | |
| CBaseAllocator *m_pAllocator; /* The allocator who owns us */ | |
| CMediaSample *m_pNext; /* Chaining in free list */ | |
| REFERENCE_TIME m_Start; /* Start sample time */ | |
| REFERENCE_TIME m_End; /* End sample time */ | |
| LONGLONG m_MediaStart; /* Real media start position */ | |
| LONG m_MediaEnd; /* A difference to get the end */ | |
| AM_MEDIA_TYPE *m_pMediaType; /* Media type change data */ | |
| DWORD m_dwStreamId; /* Stream id */ | |
| public: | |
| LONG m_cRef; /* Reference count */ | |
| public: | |
| CMediaSample( | |
| __in_opt LPCTSTR pName, | |
| __in_opt CBaseAllocator *pAllocator, | |
| __inout_opt HRESULT *phr, | |
| __in_bcount_opt(length) LPBYTE pBuffer = NULL, | |
| LONG length = 0); | |
| #ifdef UNICODE | |
| CMediaSample( | |
| __in_opt LPCSTR pName, | |
| __in_opt CBaseAllocator *pAllocator, | |
| __inout_opt HRESULT *phr, | |
| __in_bcount_opt(length) LPBYTE pBuffer = NULL, | |
| LONG length = 0); | |
| #endif | |
| virtual ~CMediaSample(); | |
| /* Note the media sample does not delegate to its owner */ | |
| STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv); | |
| STDMETHODIMP_(ULONG) AddRef(); | |
| STDMETHODIMP_(ULONG) Release(); | |
| // set the buffer pointer and length. Used by allocators that | |
| // want variable sized pointers or pointers into already-read data. | |
| // This is only available through a CMediaSample* not an IMediaSample* | |
| // and so cannot be changed by clients. | |
| HRESULT SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes); | |
| // Get me a read/write pointer to this buffer's memory. | |
| STDMETHODIMP GetPointer(__deref_out BYTE ** ppBuffer); | |
| STDMETHODIMP_(LONG) GetSize(void); | |
| // get the stream time at which this sample should start and finish. | |
| STDMETHODIMP GetTime( | |
| __out REFERENCE_TIME * pTimeStart, // put time here | |
| __out REFERENCE_TIME * pTimeEnd | |
| ); | |
| // Set the stream time at which this sample should start and finish. | |
| STDMETHODIMP SetTime( | |
| __in_opt REFERENCE_TIME * pTimeStart, // put time here | |
| __in_opt REFERENCE_TIME * pTimeEnd | |
| ); | |
| STDMETHODIMP IsSyncPoint(void); | |
| STDMETHODIMP SetSyncPoint(BOOL bIsSyncPoint); | |
| STDMETHODIMP IsPreroll(void); | |
| STDMETHODIMP SetPreroll(BOOL bIsPreroll); | |
| STDMETHODIMP_(LONG) GetActualDataLength(void); | |
| STDMETHODIMP SetActualDataLength(LONG lActual); | |
| // these allow for limited format changes in band | |
| STDMETHODIMP GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType); | |
| STDMETHODIMP SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType); | |
| // returns S_OK if there is a discontinuity in the data (this same is | |
| // not a continuation of the previous stream of data | |
| // - there has been a seek). | |
| STDMETHODIMP IsDiscontinuity(void); | |
| // set the discontinuity property - TRUE if this sample is not a | |
| // continuation, but a new sample after a seek. | |
| STDMETHODIMP SetDiscontinuity(BOOL bDiscontinuity); | |
| // get the media times for this sample | |
| STDMETHODIMP GetMediaTime( | |
| __out LONGLONG * pTimeStart, | |
| __out LONGLONG * pTimeEnd | |
| ); | |
| // Set the media times for this sample | |
| STDMETHODIMP SetMediaTime( | |
| __in_opt LONGLONG * pTimeStart, | |
| __in_opt LONGLONG * pTimeEnd | |
| ); | |
| // Set and get properties (IMediaSample2) | |
| STDMETHODIMP GetProperties( | |
| DWORD cbProperties, | |
| __out_bcount(cbProperties) BYTE * pbProperties | |
| ); | |
| STDMETHODIMP SetProperties( | |
| DWORD cbProperties, | |
| __in_bcount(cbProperties) const BYTE * pbProperties | |
| ); | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CBaseAllocator | |
| // | |
| // Abstract base class that manages a list of media samples | |
| // | |
| // This class provides support for getting buffers from the free list, | |
| // including handling of commit and (asynchronous) decommit. | |
| // | |
| // Derive from this class and override the Alloc and Free functions to | |
| // allocate your CMediaSample (or derived) objects and add them to the | |
| // free list, preparing them as necessary. | |
| //===================================================================== | |
| //===================================================================== | |
| class AM_NOVTABLE CBaseAllocator : public CUnknown,// A non delegating IUnknown | |
| public IMemAllocatorCallbackTemp, // The interface we support | |
| public CCritSec // Provides object locking | |
| { | |
| class CSampleList; | |
| friend class CSampleList; | |
| /* Trick to get at protected member in CMediaSample */ | |
| static CMediaSample * &NextSample(__in CMediaSample *pSample) | |
| { | |
| return pSample->m_pNext; | |
| }; | |
| /* Mini list class for the free list */ | |
| class CSampleList | |
| { | |
| public: | |
| CSampleList() : m_List(NULL), m_nOnList(0) {}; | |
| #ifdef DEBUG | |
| ~CSampleList() | |
| { | |
| ASSERT(m_nOnList == 0); | |
| }; | |
| #endif | |
| CMediaSample *Head() const { return m_List; }; | |
| CMediaSample *Next(__in CMediaSample *pSample) const { return CBaseAllocator::NextSample(pSample); }; | |
| int GetCount() const { return m_nOnList; }; | |
| void Add(__inout CMediaSample *pSample) | |
| { | |
| ASSERT(pSample != NULL); | |
| CBaseAllocator::NextSample(pSample) = m_List; | |
| m_List = pSample; | |
| m_nOnList++; | |
| }; | |
| CMediaSample *RemoveHead() | |
| { | |
| CMediaSample *pSample = m_List; | |
| if (pSample != NULL) { | |
| m_List = CBaseAllocator::NextSample(m_List); | |
| m_nOnList--; | |
| } | |
| return pSample; | |
| }; | |
| void Remove(__inout CMediaSample *pSample); | |
| public: | |
| CMediaSample *m_List; | |
| int m_nOnList; | |
| }; | |
| protected: | |
| CSampleList m_lFree; // Free list | |
| /* Note to overriders of CBaseAllocator. | |
| We use a lazy signalling mechanism for waiting for samples. | |
| This means we don't call the OS if no waits occur. | |
| In order to implement this: | |
| 1. When a new sample is added to m_lFree call NotifySample() which | |
| calls ReleaseSemaphore on m_hSem with a count of m_lWaiting and | |
| sets m_lWaiting to 0. | |
| This must all be done holding the allocator's critical section. | |
| 2. When waiting for a sample call SetWaiting() which increments | |
| m_lWaiting BEFORE leaving the allocator's critical section. | |
| 3. Actually wait by calling WaitForSingleObject(m_hSem, INFINITE) | |
| having left the allocator's critical section. The effect of | |
| this is to remove 1 from the semaphore's count. You MUST call | |
| this once having incremented m_lWaiting. | |
| The following are then true when the critical section is not held : | |
| (let nWaiting = number about to wait or waiting) | |
| (1) if (m_lFree.GetCount() != 0) then (m_lWaiting == 0) | |
| (2) m_lWaiting + Semaphore count == nWaiting | |
| We would deadlock if | |
| nWaiting != 0 && | |
| m_lFree.GetCount() != 0 && | |
| Semaphore count == 0 | |
| But from (1) if m_lFree.GetCount() != 0 then m_lWaiting == 0 so | |
| from (2) Semaphore count == nWaiting (which is non-0) so the | |
| deadlock can't happen. | |
| */ | |
| HANDLE m_hSem; // For signalling | |
| long m_lWaiting; // Waiting for a free element | |
| long m_lCount; // how many buffers we have agreed to provide | |
| long m_lAllocated; // how many buffers are currently allocated | |
| long m_lSize; // agreed size of each buffer | |
| long m_lAlignment; // agreed alignment | |
| long m_lPrefix; // agreed prefix (preceeds GetPointer() value) | |
| BOOL m_bChanged; // Have the buffer requirements changed | |
| // if true, we are decommitted and can't allocate memory | |
| BOOL m_bCommitted; | |
| // if true, the decommit has happened, but we haven't called Free yet | |
| // as there are still outstanding buffers | |
| BOOL m_bDecommitInProgress; | |
| // Notification interface | |
| IMemAllocatorNotifyCallbackTemp *m_pNotify; | |
| BOOL m_fEnableReleaseCallback; | |
| // called to decommit the memory when the last buffer is freed | |
| // pure virtual - need to override this | |
| virtual void Free(void) PURE; | |
| // override to allocate the memory when commit called | |
| virtual HRESULT Alloc(void); | |
| public: | |
| CBaseAllocator( | |
| __in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *, | |
| BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE); | |
| #ifdef UNICODE | |
| CBaseAllocator( | |
| __in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *, | |
| BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE); | |
| #endif | |
| virtual ~CBaseAllocator(); | |
| DECLARE_IUNKNOWN | |
| // override this to publicise our interfaces | |
| STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); | |
| STDMETHODIMP SetProperties( | |
| __in ALLOCATOR_PROPERTIES* pRequest, | |
| __out ALLOCATOR_PROPERTIES* pActual); | |
| // return the properties actually being used on this allocator | |
| STDMETHODIMP GetProperties( | |
| __out ALLOCATOR_PROPERTIES* pProps); | |
| // override Commit to allocate memory. We handle the GetBuffer | |
| //state changes | |
| STDMETHODIMP Commit(); | |
| // override this to handle the memory freeing. We handle any outstanding | |
| // GetBuffer calls | |
| STDMETHODIMP Decommit(); | |
| // get container for a sample. Blocking, synchronous call to get the | |
| // next free buffer (as represented by an IMediaSample interface). | |
| // on return, the time etc properties will be invalid, but the buffer | |
| // pointer and size will be correct. The two time parameters are | |
| // optional and either may be NULL, they may alternatively be set to | |
| // the start and end times the sample will have attached to it | |
| // bPrevFramesSkipped is not used (used only by the video renderer's | |
| // allocator where it affects quality management in direct draw). | |
| STDMETHODIMP GetBuffer(__deref_out IMediaSample **ppBuffer, | |
| __in_opt REFERENCE_TIME * pStartTime, | |
| __in_opt REFERENCE_TIME * pEndTime, | |
| DWORD dwFlags); | |
| // final release of a CMediaSample will call this | |
| STDMETHODIMP ReleaseBuffer(IMediaSample *pBuffer); | |
| // obsolete:: virtual void PutOnFreeList(CMediaSample * pSample); | |
| STDMETHODIMP SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify); | |
| STDMETHODIMP GetFreeCount(__out LONG *plBuffersFree); | |
| // Notify that a sample is available | |
| void NotifySample(); | |
| // Notify that we're waiting for a sample | |
| void SetWaiting() { m_lWaiting++; }; | |
| }; | |
| //===================================================================== | |
| //===================================================================== | |
| // Defines CMemAllocator | |
| // | |
| // this is an allocator based on CBaseAllocator that allocates sample | |
| // buffers in main memory (from 'new'). You must call SetProperties | |
| // before calling Commit. | |
| // | |
| // we don't free the memory when going into Decommit state. The simplest | |
| // way to implement this without complicating CBaseAllocator is to | |
| // have a Free() function, called to go into decommit state, that does | |
| // nothing and a ReallyFree function called from our destructor that | |
| // actually frees the memory. | |
| //===================================================================== | |
| //===================================================================== | |
| // Make me one from quartz.dll | |
| STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator); | |
| class CMemAllocator : public CBaseAllocator | |
| { | |
| protected: | |
| LPBYTE m_pBuffer; // combined memory for all buffers | |
| // override to free the memory when decommit completes | |
| // - we actually do nothing, and save the memory until deletion. | |
| void Free(void); | |
| // called from the destructor (and from Alloc if changing size/count) to | |
| // actually free up the memory | |
| void ReallyFree(void); | |
| // overriden to allocate the memory when commit called | |
| HRESULT Alloc(void); | |
| public: | |
| /* This goes in the factory template table to create new instances */ | |
| static CUnknown *CreateInstance(__inout_opt LPUNKNOWN, __inout HRESULT *); | |
| STDMETHODIMP SetProperties( | |
| __in ALLOCATOR_PROPERTIES* pRequest, | |
| __out ALLOCATOR_PROPERTIES* pActual); | |
| CMemAllocator(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *); | |
| #ifdef UNICODE | |
| CMemAllocator(__in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *); | |
| #endif | |
| ~CMemAllocator(); | |
| }; | |
| // helper used by IAMovieSetup implementation | |
| STDAPI | |
| AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata | |
| , IFilterMapper * pIFM | |
| , BOOL bRegister ); | |
| /////////////////////////////////////////////////////////////////////////// | |
| // ------------------------------------------------------------------------ | |
| // ------------------------------------------------------------------------ | |
| // ------------------------------------------------------------------------ | |
| // ------------------------------------------------------------------------ | |
| /////////////////////////////////////////////////////////////////////////// | |
| #endif /* __FILTER__ */ | |