Filter组件开发中的SDK基类分析
转载请标明是引用于 http://blog.csdn.net/chenyujing1234参考书> DirectShow SDK提供了一套开发Filter的基类源代码。基于这些基类开发Filter将大大简化开发过程。1、CBaseObject大部分SDK类都从CBaseObject类(参见combase.h)中继承而来的。class CBaseObject{p
转载请标明是引用于 http://blog.csdn.net/chenyujing1234
参考书<<DirectShow开发指南>>
DirectShow SDK提供了一套开发Filter的基类源代码。基于这些基类开发Filter将大大简化开发过程。
1、CBaseObject
大部分SDK类都从CBaseObject类(参见combase.h)中继承而来的。
class CBaseObject
{
private:
// Disable the copy constructor and assignment by default so you will get
// compiler errors instead of unexpected behaviour if you pass objects
// by value or assign objects.
CBaseObject(const CBaseObject& objectSrc); // no implementation
void operator=(const CBaseObject& objectSrc); // no implementation
private:
static LONG m_cObjects; /* Total number of objects active */
protected:
#ifdef DEBUG
DWORD m_dwCookie; /* Cookie identifying this object */
#endif
public:
/* These increment and decrement the number of active objects */
CBaseObject(const TCHAR *pName);
#ifdef UNICODE
CBaseObject(const char *pName);
#endif
~CBaseObject();
/* Call this to find if there are any CUnknown derived objects active */
static LONG ObjectsActive() {
return m_cObjects;
};
};
2、 CUnknown
作为COM组件(参见combase.cpp文件),最基本的当然是IUnknown接口的实现。SDK提供了CUnknown类,SDK实现了COM接口类都是直接或间接从这个类继承来的。
class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
public CBaseObject
{
private:
const LPUNKNOWN m_pUnknown; /* Owner of this object */
protected: /* So we can override NonDelegatingRelease() */
volatile LONG m_cRef; /* Number of reference counts */
public:
CUnknown(const TCHAR *pName, LPUNKNOWN pUnk);
virtual ~CUnknown() {};
// This is redundant, just use the other constructor
// as we never touch the HRESULT in this anyway
CUnknown(TCHAR *pName, LPUNKNOWN pUnk,HRESULT *phr);
#ifdef UNICODE
CUnknown(const char *pName, LPUNKNOWN pUnk);
CUnknown(char *pName, LPUNKNOWN pUnk,HRESULT *phr);
#endif
/* Return the owner of this object */
LPUNKNOWN GetOwner() const {
return m_pUnknown;
};
/* Called from the class factory to create a new instance, it is
pure virtual so it must be overriden in your derived class */
/* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
/* Non delegating unknown implementation */
STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
STDMETHODIMP_(ULONG) NonDelegatingRelease();
};
CUnknown类从CBaseObject中继承而来,另外CUnknown类还实现了INonDelegatingUnknown接口,用于支持引用计数、接口查询、COM组件“聚合”等。
CUnknown类的使用方法如下:
(1) 从CUnknown派生一个子类,并在子类的public区加入DECLARE_IUNKNOWN宏;
(2) 重写NonDelegatingQueryInterface函数,用以支持IUnknown外的其他接口;
(3) 在子类的构造函数中调用CUnknown的构造函数。
eg:
class CSeekingPassThru : public ISeekingPassThru, public CUnknown
{
public:
static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
CSeekingPassThru(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
~CSeekingPassThru();
DECLARE_IUNKNOWN;
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);
private:
CPosPassThru *m_pPosPassThru;
};
CSeekingPassThru::CSeekingPassThru( TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr )
: CUnknown(pName, pUnk, phr),
m_pPosPassThru(NULL)
{
}
3、 CBaseFilter
最基本的Filter由CBaseFilter 类(参见amfilter.cpp)实现。
作为Filter的基本特征,CBaseFilter实现了IBaseFilter接口(IbaseFilter从IMediaFilter继承而来)。
同时CBaseFilter还实现了Filter框架(描述了各个Pin组件的情况)。
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(
const TCHAR *pName, // Object description
LPUNKNOWN pUnk, // IUnknown of delegating object
CCritSec *pLock, // Object who maintains lock
REFCLSID clsid); // The clsid to be used to serialize this filter
CBaseFilter(
TCHAR *pName, // Object description
LPUNKNOWN pUnk, // IUnknown of delegating object
CCritSec *pLock, // Object who maintains lock
REFCLSID clsid, // The clsid to be used to serialize this filter
HRESULT *phr); // General OLE return code
#ifdef UNICODE
CBaseFilter(
const CHAR *pName, // Object description
LPUNKNOWN pUnk, // IUnknown of delegating object
CCritSec *pLock, // Object who maintains lock
REFCLSID clsid); // The clsid to be used to serialize this filter
CBaseFilter(
CHAR *pName, // Object description
LPUNKNOWN pUnk, // IUnknown of delegating object
CCritSec *pLock, // Object who maintains lock
REFCLSID clsid, // The clsid to be used to serialize this filter
HRESULT *phr); // General OLE return code
#endif
~CBaseFilter();
DECLARE_IUNKNOWN
// override this to say what interfaces we support where
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
#ifdef DEBUG
STDMETHODIMP_(ULONG) NonDelegatingRelease();
#endif
//
// --- IPersist method ---
//
STDMETHODIMP GetClassID(CLSID *pClsID);
// --- IMediaFilter methods ---
STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);
STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
STDMETHODIMP GetSyncSource(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(
IEnumPins ** ppEnum);
// default behaviour of FindPin assumes pin ids are their names
STDMETHODIMP FindPin(
LPCWSTR Id,
IPin ** ppPin
);
STDMETHODIMP QueryFilterInfo(
FILTER_INFO * pInfo);
STDMETHODIMP JoinFilterGraph(
IFilterGraph * pGraph,
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(
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
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, 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 LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; }
};
CBaseFilter类的使用方法如下:
(1) 声明一个新类是从CBaseFilter中继承而来;
(2) 在新类中定义Filter上的Pin的实例(Pin从CBasePin类继承而来);
(3) 实现纯虚函数CBaseFilter::GetPin,用于返回Filter上各个Pin的对象指针;
(4) 实现纯虚函数CBaseFilter::GetPinCount,用于返回Filter上Pin 的数量;
(5) 考虑如何处理从输入Pin进来的Sample数据。
eg:
//
// The filter object itself. Supports IBaseFilter through
// CBaseFilter and also IFileSourceFilter directly in this object
// CAsyncReader类实现了一个Filter,它从CBaseFilter派生,实现了仅含一个输出
// Pin(CAsyncOutputPin类的实例)的Source filter框架。
class CAsyncReader : public CBaseFilter
{
protected:
// filter-wide lock
CCritSec m_csFilter;
// all i/o done here
CAsyncIo m_Io;
// (2)在新类中定义Filter上的Pin的实例(Pin从CBasePin类继承而来);
// our output pin
CAsyncOutputPin m_OutputPin;
// Type we think our data is
CMediaType m_mt;
public:
// construction / destruction
CAsyncReader(
TCHAR *pName,
LPUNKNOWN pUnk,
CAsyncStream *pStream, // 它是Filter获取数据的源
HRESULT *phr);
~CAsyncReader();
//(3) 实现纯虚函数CBaseFilter::GetPin,用于返回Filter上各个Pin的对象指针;
//(4) 实现纯虚函数CBaseFilter::GetPinCount,用于返回Filter上Pin 的数量;
int GetPinCount();
CBasePin *GetPin(int n);
// --- Access our media type
const CMediaType *LoadType() const
{
return &m_mt;
}
virtual HRESULT Connect(
IPin * pReceivePin,
const AM_MEDIA_TYPE *pmt // optional media type
)
{
return m_OutputPin.CBasePin::Connect(pReceivePin, pmt);
}
};
还有SDK类的CSource、CBaseRenderer、 CTracsformFilter都是从CBaseFilter继承来的,实现开发Filter时,
使用这些子类作为Filter类。
4、CBasePin
Filter 上最基本的Pin由CBasePin类(参见 amfilter.h)实现。
作为Pin的基本特征,CBasePin实现了IPin接口。CBasePin设计了Pin 的整个连接过程。
另外,这个类还实现了IQualityControl接口,该接口用于质量控制。
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
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(
TCHAR *pObjectName, // Object description
CBaseFilter *pFilter, // Owning filter who knows about pins
CCritSec *pLock, // Object who implements the lock
HRESULT *phr, // General OLE return code
LPCWSTR pName, // Pin name for us
PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
#ifdef UNICODE
CBasePin(
CHAR *pObjectName, // Object description
CBaseFilter *pFilter, // Owning filter who knows about pins
CCritSec *pLock, // Object who implements the lock
HRESULT *phr, // General OLE return code
LPCWSTR pName, // Pin name for us
PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT
#endif
virtual ~CBasePin();
DECLARE_IUNKNOWN
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, 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,
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(IPin **pPin);
STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *pmt);
STDMETHODIMP QueryPinInfo(
PIN_INFO * pInfo
);
STDMETHODIMP QueryDirection(
PIN_DIRECTION * pPinDir
);
STDMETHODIMP QueryId(
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(
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(
IPin* *apPin, // array of IPin*
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,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();
};
在CBasePin实现的成员函数中,有3个与Filter的状态转换相对应。
// 转换pin到活动状态(暂停或运行)
// not an error to call this if already active
virtual HRESULT Active(void);
// 转换pin到不活动状态 - 可能已经处于非活动状态
virtual HRESULT Inactive(void);
// 通知运行filter 的 Run()
virtual HRESULT Run(REFERENCE_TIME tStart);
我们来看一下Filter的Stop的实现,实际上就是调用Filter的所有pin的Inactive函数
STDMETHODIMP
CBaseFilter::Stop()
{
CAutoLock cObjectLock(m_pLock);
HRESULT hr = NOERROR;
// 通知所有pin改变状态
if (m_State != State_Stopped) {
int cPins = GetPinCount();
for (int c = 0; c < cPins; c++) {
CBasePin *pPin = GetPin(c);
// Disconnected pins are not activated - this saves pins worrying
// about this state themselves. We ignore the return code to make
// sure everyone is inactivated regardless. The base input pin
// class can return an error if it has no allocator but Stop can
// be used to resync the graph state after something has gone bad
// 仅在完成连接的pin上调用Inactive函数
// 如果Inactive函数返回一个错误值,则暂时忽略,
// 以便所有Pin都有机会被调用Inactive
if (pPin->IsConnected()) {
HRESULT hrTmp = pPin->Inactive();
if (FAILED(hrTmp) && SUCCEEDED(hr)) {
hr = hrTmp;
}
}
}
}
m_State = State_Stopped;
return hr;
}
在实际开发Filter的过程中,很有可能重写CBasePin::Inactive、 CBasePin::Active和CBasePin::Run这3个函数,以进行必要的初始化、释放资源等。
CBasePin类的使用方法如下:
(1) 从CBasePin派生一个子类;
(2) 实现纯虚函数CBasePIn::CheckMediaType,进行Pin连接时的媒体类型检查;
(3) 实现纯虚函数CBasePin::GetMediaType,提供Pin上的首选媒体类型。
(4) 实现IPin::BeginFlush和IPin::EndFlush两个函数。
(5) 可能需要重写的函数包括
CBasePin::Active() 实现资源分配
CBasePin::Inactive 实现资源释放
CBasePin::Run 在Filter运行前进行一些初始化
CBasePin::CheckConnect 连接时检查,如查询对方Pin上是否支持某个特殊接口
CBasePin::BreakConnect 断开连接,并进行必要的资源释放
CBasePin::CompleteConnect 完成连接时被调用,可以在这个函数中获得当前连接的媒体类型参数
CBasePin::EndOfStream 当上流数据全部传送完毕后被调用。
如果这个是Transform Filter,则将EndOfStream继续入下传送;
如果是Renderer Filter,需要向Filter Graph Manager发送一个EC_COMPLETE事件
CBasePin::Noftify 直接响应质量控制。
eg:
// CAsyncOutputPin实现了一个输出Pin
// 继承自IAsyncReader、CBasePin,这是对拉模式的Source Filter的基本要求
/* IAsyncReader接口方法及描述如下:
BeginFlush 放弃所有正在进行的数据读取
EndFlush 与BeginFlush配对,标示Flush过程结束
Length 得到数据总长度和当前可以读取的长度
RequestAlloctor 要求一个输入Pin上的Sample管理器
Request 发出一个数据请求
SyncReadAligned 同步读取数据(边界对齐)
SyncRead 同步读取数据
WaitForNext 等待一个请求的完成
======================================================================
可以看出CAsyOutputPin类上实现的IAsyncReader的各个接口方法,都“委托”
给了CAsyncIo类对象的同名成员函数
*/
class CAsyncOutputPin
: public IAsyncReader,
public CBasePin
{
protected:
CAsyncReader* m_pReader;
CAsyncIo * m_pIo;
// This is set every time we're asked to return an IAsyncReader
// interface
// This allows us to know if the downstream pin can use
// this transport, otherwise we can hook up to thinks like the
// dump filter and nothing happens
BOOL m_bQueriedForAsyncReader;
HRESULT InitAllocator(IMemAllocator **ppAlloc);
public:
// constructor and destructor
CAsyncOutputPin(
HRESULT * phr,
CAsyncReader *pReader,
CAsyncIo *pIo,
CCritSec * pLock);
~CAsyncOutputPin();
// --- CUnknown ---
// need to expose IAsyncReader
DECLARE_IUNKNOWN
STDMETHODIMP NonDelegatingQueryInterface(REFIID, void**);
// --- IPin methods ---
STDMETHODIMP Connect(
IPin * pReceivePin,
const AM_MEDIA_TYPE *pmt // optional media type
);
// --- CBasePin methods ---
// return the types we prefer - this will return the known
// file type
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
// can we support this type?
HRESULT CheckMediaType(const CMediaType* pType);
// Clear the flag so we see if IAsyncReader is queried for
HRESULT CheckConnect(IPin *pPin)
{
m_bQueriedForAsyncReader = FALSE;
return CBasePin::CheckConnect(pPin);
}
// See if it was asked for
HRESULT CompleteConnect(IPin *pReceivePin)
{
if (m_bQueriedForAsyncReader) {
return CBasePin::CompleteConnect(pReceivePin);
} else {
#ifdef VFW_E_NO_TRANSPORT
return VFW_E_NO_TRANSPORT;
#else
return E_FAIL;
#endif
}
}
// Remove our connection status
HRESULT BreakConnect()
{
m_bQueriedForAsyncReader = FALSE;
return CBasePin::BreakConnect();
}
// --- IAsyncReader methods ---
// pass in your preferred allocator and your preferred properties.
// method returns the actual allocator to be used. Call GetProperties
// on returned allocator to learn alignment and prefix etc chosen.
// this allocator will be not be committed and decommitted by
// the async reader, only by the consumer.
STDMETHODIMP RequestAllocator(
IMemAllocator* pPreferred,
ALLOCATOR_PROPERTIES* pProps,
IMemAllocator ** ppActual);
// queue a request for data.
// media sample start and stop times contain the requested absolute
// byte position (start inclusive, stop exclusive).
// may fail if sample not obtained from agreed allocator.
// may fail if start/stop position does not match agreed alignment.
// samples allocated from source pin's allocator may fail
// GetPointer until after returning from WaitForNext.
STDMETHODIMP Request(
IMediaSample* pSample,
DWORD_PTR dwUser); // user context
// block until the next sample is completed or the timeout occurs.
// timeout (millisecs) may be 0 or INFINITE. Samples may not
// be delivered in order. If there is a read error of any sort, a
// notification will already have been sent by the source filter,
// and STDMETHODIMP will be an error.
STDMETHODIMP WaitForNext(
DWORD dwTimeout,
IMediaSample** ppSample, // completed sample
DWORD_PTR * pdwUser); // user context
// sync read of data. Sample passed in must have been acquired from
// the agreed allocator. Start and stop position must be aligned.
// equivalent to a Request/WaitForNext pair, but may avoid the
// need for a thread on the source filter.
STDMETHODIMP SyncReadAligned(
IMediaSample* pSample);
// sync read. works in stopped state as well as run state.
// need not be aligned. Will fail if read is beyond actual total
// length.
STDMETHODIMP SyncRead(
LONGLONG llPosition, // absolute file position
LONG lLength, // nr bytes required
BYTE* pBuffer); // write data here
// return total length of stream, and currently available length.
// reads for beyond the available length but within the total length will
// normally succeed but may block for a long period.
STDMETHODIMP Length(
LONGLONG* pTotal,
LONGLONG* pAvailable);
// cause all outstanding reads to return, possibly with a failure code
// (VFW_E_TIMEOUT) indicating they were cancelled.
// these are defined on IAsyncReader and IPin
STDMETHODIMP BeginFlush(void);
STDMETHODIMP EndFlush(void);
};
5、 CBaseInputPin和CBaseOutputPin
从CBasePin类派生的,也是很基本的输入或输出pin。
它们的实现可参见 amfilter.cpp
CBaseInputPin实现了IMemInputPin(用于推模式的数据传送),
而CBaseOutputPin主要完成了传送数据所使用的Sample管理器(Allocator)的协商,并重写了CBasePin::Active(用于实际的Sample内存分配)
以及CBasePin::Inactive(用于Sample内存的释放)
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(
TCHAR *pObjectName,
CBaseFilter *pFilter,
CCritSec *pLock,
HRESULT *phr,
LPCWSTR pName);
#ifdef UNICODE
CBaseInputPin(
CHAR *pObjectName,
CBaseFilter *pFilter,
CCritSec *pLock,
HRESULT *phr,
LPCWSTR pName);
#endif
virtual ~CBaseInputPin();
DECLARE_IUNKNOWN
// override this to publicise our interfaces
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
// return the allocator interface that this input pin
// would like the output pin to use
STDMETHODIMP GetAllocator(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 (
IMediaSample **pSamples,
long nSamples,
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(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;
}
};
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(
TCHAR *pObjectName,
CBaseFilter *pFilter,
CCritSec *pLock,
HRESULT *phr,
LPCWSTR pName);
#ifdef UNICODE
CBaseOutputPin(
CHAR *pObjectName,
CBaseFilter *pFilter,
CCritSec *pLock,
HRESULT *phr,
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, 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,
ALLOCATOR_PROPERTIES * ppropInputRequest
) PURE;
// returns an empty sample buffer from the allocator
virtual HRESULT GetDeliveryBuffer(IMediaSample ** ppSample,
REFERENCE_TIME * pStartTime,
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(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);
};
CBaseInputPin类的使用方法(派生一个子类,并且至少需要重写以下函数)如下:
(1) CBaseInputPin::BeginFlush
(2) CBaseInputPin::EndFlush
(3) CBaseInputPin::Receive
(4) CBaseInputPin::CheckMediaType
(5) CBaseInputPin::GetMediaType
eg:
class CRendererInputPin : public CBaseInputPin
{
protected:
CBaseRenderer *m_pRenderer;
public:
CRendererInputPin(CBaseRenderer *pRenderer,
HRESULT *phr,
LPCWSTR Name);
// Overriden from the base pin classes
HRESULT BreakConnect();
HRESULT CompleteConnect(IPin *pReceivePin);
HRESULT SetMediaType(const CMediaType *pmt);
HRESULT CheckMediaType(const CMediaType *pmt);
HRESULT Active();
HRESULT Inactive();
// Add rendering behaviour to interface functions
STDMETHODIMP QueryId(LPWSTR *Id);
STDMETHODIMP EndOfStream();
STDMETHODIMP BeginFlush();
STDMETHODIMP EndFlush();
STDMETHODIMP Receive(IMediaSample *pMediaSample);
// Helper
IMemAllocator inline *Allocator() const
{
return m_pAllocator;
}
};
CBaseOutputPin类的使用方法(派生一个子类,并且最少需要重写以下函数)如下:
(1) 重写CBasePin::CheckMediaType进行连接时媒体类型的检查;
(2) 实现纯虚函数CBaseOutputPin::DecideBufferSize,决定Sample内存的大小;
(3) 重写CBasePin::GetMediaType, 提供Pin 上的首选媒体类型。
class CTransformOutputPin : public CBaseOutputPin
{
friend class CTransformFilter;
protected:
CTransformFilter *m_pTransformFilter;
public:
// implement IMediaPosition by passing upstream
IUnknown * m_pPosition;
CTransformOutputPin(
TCHAR *pObjectName,
CTransformFilter *pTransformFilter,
HRESULT * phr,
LPCWSTR pName);
#ifdef UNICODE
CTransformOutputPin(
CHAR *pObjectName,
CTransformFilter *pTransformFilter,
HRESULT * phr,
LPCWSTR pName);
#endif
~CTransformOutputPin();
// override to expose IMediaPosition
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
// --- CBaseOutputPin ------------
STDMETHODIMP QueryId(LPWSTR * Id)
{
return AMGetWideString(L"Out", Id);
}
// Grab and release extra interfaces if required
HRESULT CheckConnect(IPin *pPin);
HRESULT BreakConnect();
HRESULT CompleteConnect(IPin *pReceivePin);
// check that we can support this output type
HRESULT CheckMediaType(const CMediaType* mtOut);
// set the connection media type
HRESULT SetMediaType(const CMediaType *pmt);
// called from CBaseOutputPin during connection to ask for
// the count and size of buffers we need.
HRESULT DecideBufferSize(
IMemAllocator * pAlloc,
ALLOCATOR_PROPERTIES *pProp);
// returns the preferred formats for a pin
HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
// inherited from IQualityControl via CBasePin
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
// Media type
public:
CMediaType& CurrentMediaType() { return m_mt; };
};
===================================================================
如果开发的是一个Transform Filter,Filter的父类很多时候都是选择CTransformFilter或CTransInPlaceFilter,这种Filter的开发相对简单。
但有时,Filter框架不得不选择CBaseFilter、 CBaseInputPin、CBaseOutputFilter等类来实现,这就有点麻烦了。
这时候可以参考CTransformFilter、CTransformInputPin、CTransformOutputPin对上述3上基类的使用,以此来指导Filter的开发。
===================================================================
6、 CSource
DirectShow SDK还提供了其他更加实用的Filter类,如:
CSource、CTransformFilter、CTransInPlaceFilter、CVideoTransformFilter、 CBaseRender、CBase Video Render等。
它们的继承关系如图:
如上图所示,CSource类(参见source.cpp的实现)直接从CaseFilter中继承而来,一般作为推模式Source Filter的父类。
CSource类的使用方法如下:
(1)从CSource类中派生一个新的Filter类;
(2)在新的Filter类的构造函数中创建各个CSourceStream类实例(CSourceStream类的构造函数会自动将该Pin加入Filter中,并在析构函数中自动删除);
(3)使用CSource::pStateLock函数返回的同步对象进行Filter对象上的多线程同步。
注意: 使用CSource作为Filter父类的Filter未必就是Source Filter。在有些开发Transform Filter的应用中,输出Pin需要使用独立的线程。(即与输入Pin上传送数据
不同的线程)传关,也可以考虑使用CSource。
eg: 参照我的另一篇文章:
class CPushSourceBitmap : public CSource
{
private:
// Constructor is private because you have to use CreateInstance
CPushSourceBitmap(IUnknown *pUnk, HRESULT *phr);
~CPushSourceBitmap();
CPushPinBitmap *m_pPin;
public:
static CUnknown * WINAPI CreateInstance(IUnknown *pUnk, HRESULT *phr);
};
CPushSourceBitmap::CPushSourceBitmap(IUnknown *pUnk, HRESULT *phr)
: CSource(NAME("PushSourceBitmap"), pUnk, CLSID_PushSourceBitmap)
{
// The pin magically adds itself to our pin array.
m_pPin = new CPushPinBitmap(phr, this);
if (phr)
{
if (m_pPin == NULL)
*phr = E_OUTOFMEMORY;
else
*phr = S_OK;
}
}
7 、 CSourceStream
CSource实际上继承自CBaseFilter,提供了一个“推”数据的能力,这种Filter至少有一个输出
Pin采用了CSourecStream类(或CSourceStream的子类)。如下图所示:
CSourceStream上实现了一个线程(CSourceStream从CAMThread类继承而来),Sample数据就是靠这个线程向一线Filter发送的。
CSourceStream类的使用方法如下:
(1)从CSourceStream派生一个输出Pin类;
(2)重写CSourceStream::GetMediaType,提供输出Pin上的首选媒体类型;
(3)重写CSourceStream::CheckMediaType,进行连续时媒体类型的检查;(可选)
(4)实现CBaseOutPin::DecideBufferSize,决定Sample内存的大小;
(5)实现CSourceStream::FillBuffer,为即将传送出去的Sample 填充数据;
(6)可选地实现CSourceStream::OnThreadCreate、CSourceSream::OnThreadDestroy、CSourceStream::OnThreadStartPlay等函数,
进行适当时节的初始化、资源管理等操作。
eg: 参照我的另一篇文章:
class CPushPinBitmap : public CSourceStream
{
protected:
int m_FramesWritten; // To track where we are in the file
BOOL m_bZeroMemory; // Do we need to clear the buffer?
CRefTime m_rtSampleTime; // The time stamp for each sample
BITMAPINFO *m_pBmi; // Pointer to the bitmap header
DWORD m_cbBitmapInfo; // Size of the bitmap header
// File opening variables
HANDLE m_hFile; // Handle returned from CreateFile
BYTE * m_pFile; // Points to beginning of file buffer
BYTE * m_pImage; // Points to pixel bits
int m_iFrameNumber;
const REFERENCE_TIME m_rtFrameLength;
CCritSec m_cSharedState; // Protects our internal state
CImageDisplay m_Display; // Figures out our media type for us
public:
CPushPinBitmap(HRESULT *phr, CSource *pFilter);
~CPushPinBitmap();
// Override the version that offers exactly one media type
HRESULT GetMediaType(CMediaType *pMediaType);
HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest);
HRESULT FillBuffer(IMediaSample *pSample);
// Quality control
// Not implemented because we aren't going in real time.
// If the file-writing filter slows the graph down, we just do nothing, which means
// wait until we're unblocked. No frames are ever dropped.
STDMETHODIMP Notify(IBaseFilter *pSelf, Quality q)
{
return E_FAIL;
}
};
8、 CTransformFilter
CTransformFilter类是开发Transform Filter最基本的类,也是最常用到的类。结构如下:
它有一个输入Pin和一个输出Pin,分别使用CTransformInputPin类和CTransformOutputPin类。
从图4.8和图4.9可以看出,
CTransformFilter从CBaseFilter继承而来,
CTransformInputPin从CBaseInputPin继承而来,
CTransformOutputPin从CBaseOutputPin继承而来。另个,在CTransformOutputPin上还实现了IMdiaSeeking 和 IMediaPosition接口。
(其实,CTransformOutputPin并没有真正实现各个Seek操作。在实际的Seek操作请发生时,CTransformOutpPin会将这些操作请求转发给上一级Filter的输出Pin)。
CTransformFilter实现的最大特征是,它将Pin上必须实现的函数都“委托”到了Filter上(Pin上必须实现的函数在Filter上有类似的函数定义)。
一般无须重写输入和输出Pin类,而只须在Filter上实现相应的函数就行了)。
提示:CTransformFilter默认在GetPin函数实现中创建输入和输出Pin。因此,如果重写了自己的输入或输出Pin类,需要重写GetPin函数。
CTransformFilter类的使用方法(派生一个Filter子类,且最少需要重写以下函数):
(1)CTransformFilter::CheckInputType
(2)CTransformFilter::CheckTransform
(3)CTransformFilter::DecideBufferSize
(4)CTransformFilter::GetMeiaType
(5)CTransformFilter::Transform
eg:CVideoTransformFilter虽然没有实现上面五个函数,但CVideoTransformFilter 的继承类去实现它们。
class CVideoTransformFilter : public CTransformFilter
{
public:
CVideoTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid);
~CVideoTransformFilter();
HRESULT EndFlush();
// =================================================================
// ----- override these bits ---------------------------------------
// =================================================================
// The following methods are in CTransformFilter which is inherited.
// They are mentioned here for completeness
//
// These MUST be supplied in a derived class
//
// NOTE:
// virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
// virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
// virtual HRESULT CheckTransform
// (const CMediaType* mtIn, const CMediaType* mtOut) PURE;
// static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
// virtual HRESULT DecideBufferSize
// (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE;
// virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
//
// These MAY also be overridden
//
// virtual HRESULT StopStreaming();
// virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
// virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
// virtual HRESULT BreakConnect(PIN_DIRECTION dir);
// virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
// virtual HRESULT EndOfStream(void);
// virtual HRESULT BeginFlush(void);
// virtual HRESULT EndFlush(void);
// virtual HRESULT NewSegment
// (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate);
#ifdef PERF
// If you override this - ensure that you register all these ids
// as well as any of your own,
virtual void RegisterPerfId() {
m_idSkip = MSR_REGISTER(TEXT("Video Transform Skip frame"));
m_idFrameType = MSR_REGISTER(TEXT("Video transform frame type"));
m_idLate = MSR_REGISTER(TEXT("Video Transform Lateness"));
m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key"));
CTransformFilter::RegisterPerfId();
}
#endif
protected:
// =========== QUALITY MANAGEMENT IMPLEMENTATION ========================
// Frames are assumed to come in three types:
// Type 1: an AVI key frame or an MPEG I frame.
// This frame can be decoded with no history.
// Dropping this frame means that no further frame can be decoded
// until the next type 1 frame.
// Type 1 frames are sync points.
// Type 2: an AVI non-key frame or an MPEG P frame.
// This frame cannot be decoded unless the previous type 1 frame was
// decoded and all type 2 frames since have been decoded.
// Dropping this frame means that no further frame can be decoded
// until the next type 1 frame.
// Type 3: An MPEG B frame.
// This frame cannot be decoded unless the previous type 1 or 2 frame
// has been decoded AND the subsequent type 1 or 2 frame has also
// been decoded. (This requires decoding the frames out of sequence).
// Dropping this frame affects no other frames. This implementation
// does not allow for these. All non-sync-point frames are treated
// as being type 2.
//
// The spacing of frames of type 1 in a file is not guaranteed. There MUST
// be a type 1 frame at (well, near) the start of the file in order to start
// decoding at all. After that there could be one every half second or so,
// there could be one at the start of each scene (aka "cut", "shot") or
// there could be no more at all.
// If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED
// without losing all the rest of the movie. There is no way to tell whether
// this is the case, so we find that we are in the gambling business.
// To try to improve the odds, we record the greatest interval between type 1s
// that we have seen and we bet on things being no worse than this in the
// future.
// You can tell if it's a type 1 frame by calling IsSyncPoint().
// there is no architected way to test for a type 3, so you should override
// the quality management here if you have B-frames.
int m_nKeyFramePeriod; // the largest observed interval between type 1 frames
// 1 means every frame is type 1, 2 means every other.
int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1.
// becomes the new m_nKeyFramePeriod if greater.
BOOL m_bSkipping; // we are skipping to the next type 1 frame
#ifdef PERF
int m_idFrameType; // MSR id Frame type. 1=Key, 2="non-key"
int m_idSkip; // MSR id skipping
int m_idLate; // MSR id lateness
int m_idTimeTillKey; // MSR id for guessed time till next key frame.
#endif
virtual HRESULT StartStreaming();
HRESULT AbortPlayback(HRESULT hr); // if something bad happens
HRESULT Receive(IMediaSample *pSample);
HRESULT AlterQuality(Quality q);
BOOL ShouldSkipFrame(IMediaSample * pIn);
int m_itrLate; // lateness from last Quality message
// (this overflows at 214 secs late).
int m_tDecodeStart; // timeGetTime when decode started.
int m_itrAvgDecode; // Average decode time in reference units.
BOOL m_bNoSkip; // debug - no skipping.
// We send an EC_QUALITY_CHANGE notification to the app if we have to degrade.
// We send one when we start degrading, not one for every frame, this means
// we track whether we've sent one yet.
BOOL m_bQualityChanged;
// When non-zero, don't pass anything to renderer until next keyframe
// If there are few keys, give up and eventually draw something
int m_nWaitForKey;
};
9、 CTransInPlaceFilter
CTransInPlaceFilter是一个“就地”处理的Transform Filter类。结构如下:
与CTransformFilter,CTransInPlaceFilter也有一个输入Pin和一个输出Pin,但使用CTransInPlaceOutputPin类。
CTransInPlaceFilter的输入和输出Pin上一般使用相同的媒体类型进行连接,并且使用同一个Sample管理器(如果Filter实现时要修改Sample数据,
而协商达成一致的Sample管理器只读的,那么CTransInPlaceFilter的输入和输出Pin将不得不使用各自的Sample管理器)。
CTransInPlaceFilter类要实现上述的目标,主要依赖于CTransInPlaceFilter::CompleteConnect、CTransInPlaceInputPin::GetAllocator和
CTransInPlaceInputPin::NotifyAlocator的函数实现。代码如下:
// 当输入或输出Pin完成连接时被调用,
// 经过一个反复重连的过程,来达到输入和输出Pin使用相同的媒体类型的目的
HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin)
{
UNREFERENCED_PARAMETER(pReceivePin);
ASSERT(m_pInput);
ASSERT(m_pOutput);
// if we are not part of a graph, then don't indirect the pointer
// this probably prevents use of the filter without a filtergraph
if (!m_pGraph) {
return VFW_E_NOT_IN_GRAPH;
}
// Always reconnect the input to account for buffering changes
//
// Because we don't get to suggest a type on ReceiveConnection
// we need another way of making sure the right type gets used.
//
// One way would be to have our EnumMediaTypes return our output
// connection type first but more deterministic and simple is to
// call ReconnectEx passing the type we want to reconnect with
// via the base class ReconeectPin method.
// 当输出Pin调用该函数(并且此时输入Pin已连上)时,使用输出Pin上的媒体类型对
// 输入Pin进行重连接
if (dir == PINDIR_OUTPUT) {
if( m_pInput->IsConnected() ) {
return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() );
}
return NOERROR;
}
ASSERT(dir == PINDIR_INPUT);
// Reconnect output if necessary
// 当输入Pin调用该函数(并且此时输出Pin已连上)时,如果输入和输出Pin上使用的
// 媒体类型不一致,则使用输入Pin上的媒体类型对输出Pin进行重新连接
if( m_pOutput->IsConnected() ) {
if ( m_pInput->CurrentMediaType()
!= m_pOutput->CurrentMediaType()
) {
return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() );
}
}
return NOERROR;
} // ComnpleteConnect
// 当上一级Filter的输出Pin要求我们的输入Pin提供Sample管理器时,
// 如果我们的输出Pin已连接上,则可以取出输出Pin上的Sample管理器提供给上一级
// Filter,以此达到我们的输入和输出Pin使用同一个Sample管理器的目的
STDMETHODIMP CTransInPlaceInputPin::GetAllocator(IMemAllocator ** ppAllocator)
{
CheckPointer(ppAllocator,E_POINTER);
ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
CAutoLock cObjectLock(m_pLock);
HRESULT hr;
if ( m_pTIPFilter->m_pOutput->IsConnected() ) {
// Store the allocator we got
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
->GetAllocator( ppAllocator );
if (SUCCEEDED(hr)) {
m_pTIPFilter->OutputPin()->SetAllocator( *ppAllocator );
}
}
else {
// Help upstream filter (eg TIP filter which is having to do a copy)
// by providing a temp allocator here - we'll never use
// this allocator because when our output is connected we'll
// reconnect this pin
hr = CTransformInputPin::GetAllocator( ppAllocator );
}
return hr;
} // GetAllocator
// 上一级Filter调用该函数,告知输入Pin上到底使用哪一个Sample管理器
// 如果设置进来的Sample管理器是只读的,而我们在Filter中又想修改数据,
// 则我们的Filter不得不最终使用不同的Sample管理器
STDMETHODIMP
CTransInPlaceInputPin::NotifyAllocator(
IMemAllocator * pAllocator,
BOOL bReadOnly)
{
HRESULT hr = S_OK;
CheckPointer(pAllocator,E_POINTER);
ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
CAutoLock cObjectLock(m_pLock);
m_bReadOnly = bReadOnly;
// If we modify data then don't accept the allocator if it's
// the same as the output pin's allocator
// If our output is not connected just accept the allocator
// We're never going to use this allocator because when our
// output pin is connected we'll reconnect this pin
if (!m_pTIPFilter->OutputPin()->IsConnected()) {
return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly);
}
// If the allocator is read-only and we're modifying data
// and the allocator is the same as the output pin's
// then reject
if (bReadOnly && m_pTIPFilter->m_bModifiesData) {
IMemAllocator *pOutputAllocator =
m_pTIPFilter->OutputPin()->PeekAllocator();
// Make sure we have an output allocator
if (pOutputAllocator == NULL) {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->
GetAllocator(&pOutputAllocator);
if(FAILED(hr)) {
hr = CreateMemoryAllocator(&pOutputAllocator);
}
if (SUCCEEDED(hr)) {
m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator);
pOutputAllocator->Release();
}
}
if (pAllocator == pOutputAllocator) {
hr = E_FAIL;
} else if(SUCCEEDED(hr)) {
// Must copy so set the allocator properties on the output
ALLOCATOR_PROPERTIES Props, Actual;
hr = pAllocator->GetProperties(&Props);
if (SUCCEEDED(hr)) {
hr = pOutputAllocator->SetProperties(&Props, &Actual);
}
if (SUCCEEDED(hr)) {
if ( (Props.cBuffers > Actual.cBuffers)
|| (Props.cbBuffer > Actual.cbBuffer)
|| (Props.cbAlign > Actual.cbAlign)
) {
hr = E_FAIL;
}
}
// Set the allocator on the output pin
if (SUCCEEDED(hr)) {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
->NotifyAllocator( pOutputAllocator, FALSE );
}
}
} else {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
->NotifyAllocator( pAllocator, bReadOnly );
if (SUCCEEDED(hr)) {
m_pTIPFilter->OutputPin()->SetAllocator( pAllocator );
}
}
if (SUCCEEDED(hr)) {
// It's possible that the old and the new are the same thing.
// AddRef before release ensures that we don't unload it.
pAllocator->AddRef();
if( m_pAllocator != NULL )
m_pAllocator->Release();
m_pAllocator = pAllocator; // We have an allocator for the input pin
}
return hr;
} // NotifyAllocator
CTransInPlaceFilter类定义了一个成员变量m_bModifiesData ,用于指示我们在Filter中是否会修改Saple 数据。
这个变量在CTransInPlaceFilter构造函数调用时被默认初始化为true。如果我们确定不会在Filter中修改Sample数据,
那么,将m_bModifiesData设置为false, 可以保证输入和输出Pin连接完成后使用同一个Sample管理器。
10、 CVideoTransformFilter
CVieoTransformFilter是一个实现了视频的质量控制的Transform Filter类。其结构如下:
CVieoTransformFilter通过输入Pin上的Receive 函数接收Sample时,能够根据质量消息决定是否丢帧。这个类主要是为开发AVI解码Filter而设计的。
CVieoTransformFilter类的使用基本上与CTransformFilter相同。
11、 CBaseRenderer
CBaseRender是最基本的实现Renderer Filter的类。它默认实现了一个使用CRendererInputPin类的输入Pin(Renderer Filter没有输出Pin)。
这两个类的结构如下:
从图中可以看出,CBaseRenderer从CBaseFilter继承而来。另外,CBaseRenderer上还实现了IMediaSeekin和IMediaPosition接口。
CRendererInputPin从CBaseInputPin 继承而来,它把各个主要函数调用都“委托”到Filter上。值得注意的是,当输入Pin接收到EndOfStream调用时,
Renderer Filter 有责任向Filter Graph Manager发送一个EC_COMPLETE事件。
CBaseRenderer类的使用方法(派生一个子类,并至少实现如下函数)如下:
(1)CBaseRenderer::CheckMediaType,用于检查输入Pin连接用的媒体类型;
(2)CBaseRenderer::DoRenderSample,处理当前的Sample。
提示:CBaseRenderer实际上是为了用于播放的Render Filter设计的,对于Sample的安排比较复杂。
如果我们要开发Renderer Filter不播放Sample(比如写文件的Filter、或者负责网络的Filter),Fitler的基类可以选择CBaseFilter,而此时输入Pin最
好选择CRenderedInputPin类派生。
12、CBaseVideoRenderer
CBaseVideoRenderer是一个实现Video Renderer类的基类,结构如下:
CBaseVideoRenderer在CBaseRenderer的基础上增加了IQualityControl和IQualProp接口的实现。
其中IQualityControl用于视频质量控制,IQualProp用于在Filter属性页显示一些实时性能参数。CBaseVideoRenderer类的使用方法基本上与CBaseRenderer相同。
在DirectShow SDK基类库中,除了上述Filter和Pin类外,还有很多工具类,有了这些类的支持,我们开发Fitler或DirectShow应用程序会更加轻松。
这些类包括: CPullPin、 COutputQueue、 CSourceSeeking 、CEnumPins、 CEnumMediaTypes 、CMemAllocator 、 CMediaSample 、
CBaseReferenceClock 、CMediaType、 CBasePropertyPage 等。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)