DirectShow SDK提供了一套开发Filter的基类源代码。基于这些基类开发Filter将大大简化开发过程。
1、CBaseObject
大部分SDK类都从CBaseObject类(参见combase.h)中继承而来的。
[cpp] view plaincopy
- 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接口类都是直接或间接从这个类继承来的。
[cpp] view plaincopy
- 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:
[cpp] view plaincopy
- 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;
- };
[cpp] view plaincopy
- 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组件的情况)。
[cpp] view plaincopy
- 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:
[cpp] view plaincopy
- //
- // 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接口,该接口用于质量控制。
[cpp] view plaincopy
- 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的状态转换相对应。
[cpp] view plaincopy
- // 转换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函数
[cpp] view plaincopy
- 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:
[cpp] view plaincopy
- // 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内存的释放)
[cpp] view plaincopy
- 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;
- }
- };
[cpp] view plaincopy
- 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:
[cpp] view plaincopy
- 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 上的首选媒体类型。
[cpp] view plaincopy
- 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: 参照我的另一篇文章:
[cpp] view plaincopy
- 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);
- };
[cpp] view plaincopy
- 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: 参照我的另一篇文章:
[cpp] view plaincopy
- 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 的继承类去实现它们。
[cpp] view plaincopy
- 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的函数实现。代码如下:
[cpp] view plaincopy
- // 当输入或输出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
[cpp] view plaincopy
- // 当上一级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
[cpp] view plaincopy
- // 上一级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 等。