技术标签: filter  reference  output  methods  properties  TCPMP/DDShow/音视频编解码技术  object  




DirectShow SDK提供了一套开发Filter的基类源代码。基于这些基类开发Filter将大大简化开发过程。



class CBaseObject


    // 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

    static LONG m_cObjects;     /* Total number of objects active */

#ifdef DEBUG
    DWORD m_dwCookie;           /* Cookie identifying this object */


    /* These increment and decrement the number of active objects */

    CBaseObject(const TCHAR *pName);
#ifdef UNICODE
    CBaseObject(const char *pName);

    /* Call this to find if there are any CUnknown derived objects active */

    static LONG ObjectsActive() {
        return m_cObjects;

2、 CUnknown


class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
                 public CBaseObject
    const LPUNKNOWN m_pUnknown; /* Owner of this object */

protected:                      /* So we can override NonDelegatingRelease() */
    volatile LONG m_cRef;       /* Number of reference counts */


    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);

    /* 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();



(1) 从CUnknown派生一个子类,并在子类的public区加入DECLARE_IUNKNOWN宏;

(2) 重写NonDelegatingQueryInterface函数,用以支持IUnknown外的其他接口;

(3) 在子类的构造函数中调用CUnknown的构造函数。


class CSeekingPassThru : public ISeekingPassThru, public CUnknown
    static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
    CSeekingPassThru(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);

    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

    STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);

    CPosPassThru              *m_pPosPassThru;


CSeekingPassThru::CSeekingPassThru( TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr )
                            : CUnknown(pName, pUnk, phr),

3、 CBaseFilter

最基本的Filter由CBaseFilter 类(参见amfilter.cpp)实现。



class AM_NOVTABLE CBaseFilter : public CUnknown,        // Handles an IUnknown
                    public IBaseFilter,     // The Filter Interface
                    public IAMovieSetup     // For un/registration

friend class CBasePin;

    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


        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

        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
        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

        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


    // override this to say what interfaces we support where
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
#ifdef DEBUG
    STDMETHODIMP_(ULONG) NonDelegatingRelease();

    // --- IPersist method ---


    // --- IMediaFilter methods ---


    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.

    // the start parameter is the difference to be added to the
    // sample's stream time to get the reference time for
    // its presentation

    // --- 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
                    IEnumPins ** ppEnum);

    // default behaviour of FindPin assumes pin ids are their names
        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; }



(1) 声明一个新类是从CBaseFilter中继承而来;

(2) 在新类中定义Filter上的Pin的实例(Pin从CBasePin类继承而来);

(3) 实现纯虚函数CBaseFilter::GetPin,用于返回Filter上各个Pin的对象指针;

(4) 实现纯虚函数CBaseFilter::GetPinCount,用于返回Filter上Pin 的数量;

(5) 考虑如何处理从输入Pin进来的Sample数据。


// 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

    // 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;

    // construction / destruction

        TCHAR *pName,
        LPUNKNOWN pUnk,
        CAsyncStream *pStream, // 它是Filter获取数据的源
        HRESULT *phr);


	//(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  上最基本的Pin由CBasePin类(参见 amfilter.h)实现。

作为Pin的基本特征,CBasePin实现了IPin接口。CBasePin设计了Pin 的整个连接过程。


class  AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl


    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

    // displays pin connection information

#ifdef DEBUG
    void DisplayPinInfo(IPin *pReceivePin);
    void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt);
    void DisplayPinInfo(IPin *pReceivePin) {};
    void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {};

    // 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)
        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


        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
        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
    virtual ~CBasePin();


    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).
        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

        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;

    STDMETHODIMP DisconnectInternal();


  // 转换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);



    CAutoLock cObjectLock(m_pLock);

    // 通知所有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个函数,以进行必要的初始化、释放资源等。


(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  直接响应质量控制。


// CAsyncOutputPin实现了一个输出Pin
// 继承自IAsyncReader、CBasePin,这是对拉模式的Source Filter的基本要求
/* IAsyncReader接口方法及描述如下:
BeginFlush  放弃所有正在进行的数据读取
EndFlush    与BeginFlush配对,标示Flush过程结束
Length      得到数据总长度和当前可以读取的长度
RequestAlloctor 要求一个输入Pin上的Sample管理器
Request     发出一个数据请求
SyncReadAligned 同步读取数据(边界对齐)
SyncRead    同步读取数据
WaitForNext 等待一个请求的完成
class CAsyncOutputPin
  : public IAsyncReader, 
    public CBasePin
    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);

    // constructor and destructor
        HRESULT * phr,
        CAsyncReader *pReader,
        CAsyncIo *pIo,
        CCritSec * pLock);


    // --- CUnknown ---

    // need to expose IAsyncReader
    STDMETHODIMP NonDelegatingQueryInterface(REFIID, void**);

    // --- IPin methods ---
        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 {

            return VFW_E_NO_TRANSPORT;
            return E_FAIL;

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


它们的实现可参见 amfilter.cpp




class AM_NOVTABLE CBaseInputPin : public CBasePin,
                                  public IMemInputPin


    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;


        TCHAR *pObjectName,
        CBaseFilter *pFilter,
        CCritSec *pLock,
        HRESULT *phr,
        LPCWSTR pName);
#ifdef UNICODE
        CHAR *pObjectName,
        CBaseFilter *pFilter,
        CCritSec *pLock,
        HRESULT *phr,
        LPCWSTR pName);
    virtual ~CBaseInputPin();


    // 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


    IMemAllocator *m_pAllocator;
    IMemInputPin *m_pInputPin;        // interface on the downstreaminput pin
                                      // set up in CheckConnect when we connect.


        TCHAR *pObjectName,
        CBaseFilter *pFilter,
        CCritSec *pLock,
        HRESULT *phr,
        LPCWSTR pName);
#ifdef UNICODE
        CHAR *pObjectName,
        CBaseFilter *pFilter,
        CCritSec *pLock,
        HRESULT *phr,
        LPCWSTR pName);
    // 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);


(1) CBaseInputPin::BeginFlush

(2) CBaseInputPin::EndFlush

(3) CBaseInputPin::Receive

(4) CBaseInputPin::CheckMediaType

(5) CBaseInputPin::GetMediaType


class CRendererInputPin : public CBaseInputPin

    CBaseRenderer *m_pRenderer;


    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 EndOfStream();
    STDMETHODIMP BeginFlush();
    STDMETHODIMP EndFlush();
    STDMETHODIMP Receive(IMediaSample *pMediaSample);

    // Helper
    IMemAllocator inline *Allocator() const
        return m_pAllocator;



(1) 重写CBasePin::CheckMediaType进行连接时媒体类型的检查;

(2) 实现纯虚函数CBaseOutputPin::DecideBufferSize,决定Sample内存的大小;

(3) 重写CBasePin::GetMediaType, 提供Pin 上的首选媒体类型。


class CTransformOutputPin : public CBaseOutputPin
    friend class CTransformFilter;

    CTransformFilter *m_pTransformFilter;


    // implement IMediaPosition by passing upstream
    IUnknown * m_pPosition;

        TCHAR *pObjectName,
        CTransformFilter *pTransformFilter,
        HRESULT * phr,
        LPCWSTR pName);
#ifdef UNICODE
        CHAR *pObjectName,
        CTransformFilter *pTransformFilter,
        HRESULT * phr,
        LPCWSTR pName);

    // override to expose IMediaPosition
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);

    // --- CBaseOutputPin ------------

        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
    CMediaType& CurrentMediaType() { return m_mt; };


如果开发的是一个Transform Filter,Filter的父类很多时候都是选择CTransformFilter或CTransInPlaceFilter,这种Filter的开发相对简单。

但有时,Filter框架不得不选择CBaseFilter、 CBaseInputPin、CBaseOutputFilter等类来实现,这就有点麻烦了。



6、 CSource

DirectShow SDK还提供了其他更加实用的Filter类,如:

CSource、CTransformFilter、CTransInPlaceFilter、CVideoTransformFilter、 CBaseRender、CBase Video Render等。


如上图所示,CSource类(参见source.cpp的实现)直接从CaseFilter中继承而来,一般作为推模式Source Filter的父类





注意: 使用CSource作为Filter父类的Filter未必就是Source Filter。在有些开发Transform Filter的应用中,输出Pin需要使用独立的线程。(即与输入Pin上传送数据


eg: 参照我的另一篇文章:  

class CPushSourceBitmap : public CSource

    // Constructor is private because you have to use CreateInstance
    CPushSourceBitmap(IUnknown *pUnk, HRESULT *phr);

    CPushPinBitmap *m_pPin;

    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;
            *phr = S_OK;

7 、  CSourceStream









(5)实现CSourceStream::FillBuffer,为即将传送出去的Sample 填充数据;



eg: 参照我的另一篇文章:  

class CPushPinBitmap : public CSourceStream

    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


    CPushPinBitmap(HRESULT *phr, CSource *pFilter);

    // 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最基本的类,也是最常用到的类。结构如下:






CTransformOutputPin从CBaseOutputPin继承而来。另个,在CTransformOutputPin上还实现了IMdiaSeeking 和 IMediaPosition接口。











eg:CVideoTransformFilter虽然没有实现上面五个函数,但CVideoTransformFilter 的继承类去实现它们。

class CVideoTransformFilter : public CTransformFilter

    CVideoTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid);
    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"));


    // =========== 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.

    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类。结构如下:







// 当输入或输出Pin完成连接时被调用,
// 经过一个反复重连的过程,来达到输入和输出Pin使用相同的媒体类型的目的
HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin)

    // 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;


    // 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)
    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管理器
    IMemAllocator * pAllocator,
    BOOL bReadOnly)
    HRESULT hr = S_OK;

    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 =

        //  Make sure we have an output allocator
        if (pOutputAllocator == NULL) {
            hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->
            if(FAILED(hr)) {
                hr = CreateMemoryAllocator(&pOutputAllocator);
            if (SUCCEEDED(hr)) {
        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.

        if( m_pAllocator != NULL )

        m_pAllocator = pAllocator;    // We have an allocator for the input pin

    return hr;

} // NotifyAllocator

CTransInPlaceFilter类定义了一个成员变量m_bModifiesData ,用于指示我们在Filter中是否会修改Saple 数据。


那么,将m_bModifiesData设置为false, 可以保证输入和输出Pin连接完成后使用同一个Sample管理器。


10、 CVideoTransformFilter

CVieoTransformFilter是一个实现了视频的质量控制的Transform Filter类。其结构如下:

CVieoTransformFilter通过输入Pin上的Receive 函数接收Sample时,能够根据质量消息决定是否丢帧。这个类主要是为开发AVI解码Filter而设计的。



11、 CBaseRenderer

CBaseRender是最基本的实现Renderer Filter的类。它默认实现了一个使用CRendererInputPin类的输入Pin(Renderer Filter没有输出Pin)。



CRendererInputPin从CBaseInputPin 继承而来,它把各个主要函数调用都“委托”到Filter上。值得注意的是,当输入Pin接收到EndOfStream调用时,

Renderer Filter 有责任向Filter Graph Manager发送一个EC_COMPLETE事件。




提示:CBaseRenderer实际上是为了用于播放的Render Filter设计的,对于Sample的安排比较复杂

如果我们要开发Renderer Filter不播放Sample(比如写文件的Filter、或者负责网络的Filter),Fitler的基类可以选择CBaseFilter,而此时输入Pin最




CBaseVideoRenderer是一个实现Video Renderer类的基类,结构如下:




在DirectShow SDK基类库中,除了上述Filter和Pin类外,还有很多工具类,有了这些类的支持,我们开发Fitler或DirectShow应用程序会更加轻松。

这些类包括: CPullPin、 COutputQueue、  CSourceSeeking  、CEnumPins、 CEnumMediaTypes  、CMemAllocator 、 CMediaSample  、

CBaseReferenceClock  、CMediaType、 CBasePropertyPage  等。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


【CDQ分治+FFT】LGP4566 [CTSC2018]青蕈领主_5342: [ctsc2018]青蕈领主_Dream_Lolita的博客-程序员宅基地

【题目】原题地址有一个长度为nnn的排列aaa,给定lil_ili​,表示ai−li+1…aia_{i-l_i+1} \dots a_iai−li​+1​…ai​在排序后公差为111,且lil_ili​为满足条件的最小数字,求方案数。n≤5×104n\leq 5\times 10^4n≤5×104【解题思路】以下思路来自大佬的博客神仙题。很容易证明所有的连续区间只能相离或者相互包含,不..._5342: [ctsc2018]青蕈领主


@echo offset path=C:2link路径set file=%~nx1 ; 文件及文件扩展名set ext=%~x1 ; 文件扩展名 即文件类型echo 下载%file%echo ext is %ext%IF %ext%==.bin goto download_bin ;判断文件类型IF %ext%==.hex goto download_hex


1. ViewGroup &lt;p&gt;A ViewGroup is a special view that can contain other views (called children.) The view group is the base class for layouts and views containers. This class also defines the ViewG..._android测量仪实现







本期推荐软件名称: Adguard运行平台:电脑端windows正文教程01不知道大家有没有被电脑上软件的各种广告和网站上的各种广告轰炸过,在下是身有体会!下了某些软件,时不时桌面给你来个弹窗新闻,时不时右下角给你来个游戏广告,特别进网站的时候,遇到那些恶心的弹窗广告,一秒弹出来十几个!苦不堪言啊,本款资源让你屏蔽一切广告,什么视频广告拉,弹窗广告拉等等等等,你能想到的它都能给屏蔽掉,觉得牛皮就快拿去用吧!..._adhoc屏蔽广告


关于杀死Process进程大概有如下方法: 1:android.os.Process.killProcess(pid); 2:activityManager.killBackgroundProcesses(pkgName); 3:kill -9 pid 4:am force-stop

java调用腾讯云接口-第一篇(语音异步识别)_import asyncclient from 'tencentcloud-cls-sdk-js_叶梓啊的博客-程序员宅基地

1.语音转文本实时(需要本地文件)1.1加依赖(不确定是否需要2.1中的依赖)<!-- 统一版本管理中--> <tencentcloud-speech.version>1.0.14</tencentcloud-speech.version><!--腾讯语音转文本--><dependency> <groupId>com.tencentcloudapi</groupId> <artifa_import asyncclient from 'tencentcloud-cls-sdk-js

HTML5 离线缓存_p312011150的博客-程序员宅基地

HTML5 离线缓存离线资源缓存 为了能够让用户在离线状态下继续访问 Web 应用,开发者需要提供一个 cache manifest 文件。这个文件中列出了所有需要在离线状态下使用的资源,浏览器会把这些资源缓存到本地。本节先通过一个例子展示 cache manifest 文件的用途,然后详细描述其书写方法,最后说明缓存的更新方式。//在IIS下最好使用


LabVIEW网口通讯西门子PLC,支持200、300、1200、1500、400、SMART全系列PLC常用功能一网打尽。NETCOM通讯。1.命令帧读写。程序源码,命令帧文本编写,不调用dll,不安装插件,完胜OPC 等。创作不易,非诚勿扰。谢谢大家。编号:7649668389753044LabVIEW工业控制..._labview和200smart以太网通讯


本文摘自简书用户“码农历险记”的文章。CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。1. CountDownLatch原理CountDownLatch是通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就可以恢复执行任务。2. 使用步骤Main thread startCreate CountDownLatc_java同步块countdownlatch使用方法