mirror of https://github.com/PCSX2/pcsx2.git
1588 lines
56 KiB
C++
1588 lines
56 KiB
C++
//------------------------------------------------------------------------------
|
|
// 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__ */
|
|
|
|
|
|
|