Filter组件开发中的SDK基类分析

时间:2023-12-31 20:18:44

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

1、CBaseObject

大部分SDK类都从CBaseObject类(参见combase.h)中继承而来的。

  1. class CBaseObject
  2. {
  3. private:
  4. // Disable the copy constructor and assignment by default so you will get
  5. //   compiler errors instead of unexpected behaviour if you pass objects
  6. //   by value or assign objects.
  7. CBaseObject(const CBaseObject& objectSrc);          // no implementation
  8. void operator=(const CBaseObject& objectSrc);       // no implementation
  9. private:
  10. static LONG m_cObjects;     /* Total number of objects active */
  11. protected:
  12. #ifdef DEBUG
  13. DWORD m_dwCookie;           /* Cookie identifying this object */
  14. #endif
  15. public:
  16. /* These increment and decrement the number of active objects */
  17. CBaseObject(const TCHAR *pName);
  18. #ifdef UNICODE
  19. CBaseObject(const char *pName);
  20. #endif
  21. ~CBaseObject();
  22. /* Call this to find if there are any CUnknown derived objects active */
  23. static LONG ObjectsActive() {
  24. return m_cObjects;
  25. };
  26. };
2、 CUnknown

作为COM组件(参见combase.cpp文件),最基本的当然是IUnknown接口的实现。SDK提供了CUnknown类,SDK实现了COM接口类都是直接或间接从这个类继承来的。

  1. class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
  2. public CBaseObject
  3. {
  4. private:
  5. const LPUNKNOWN m_pUnknown; /* Owner of this object */
  6. protected:                      /* So we can override NonDelegatingRelease() */
  7. volatile LONG m_cRef;       /* Number of reference counts */
  8. public:
  9. CUnknown(const TCHAR *pName, LPUNKNOWN pUnk);
  10. virtual ~CUnknown() {};
  11. // This is redundant, just use the other constructor
  12. //   as we never touch the HRESULT in this anyway
  13. CUnknown(TCHAR *pName, LPUNKNOWN pUnk,HRESULT *phr);
  14. #ifdef UNICODE
  15. CUnknown(const char *pName, LPUNKNOWN pUnk);
  16. CUnknown(char *pName, LPUNKNOWN pUnk,HRESULT *phr);
  17. #endif
  18. /* Return the owner of this object */
  19. LPUNKNOWN GetOwner() const {
  20. return m_pUnknown;
  21. };
  22. /* Called from the class factory to create a new instance, it is
  23. pure virtual so it must be overriden in your derived class */
  24. /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
  25. /* Non delegating unknown implementation */
  26. STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
  27. STDMETHODIMP_(ULONG) NonDelegatingAddRef();
  28. STDMETHODIMP_(ULONG) NonDelegatingRelease();
  29. };

CUnknown类从CBaseObject中继承而来,另外CUnknown类还实现了INonDelegatingUnknown接口,用于支持引用计数、接口查询、COM组件“聚合”等。

Filter组件开发中的SDK基类分析

CUnknown类的使用方法如下:

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

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

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

eg:

  1. class CSeekingPassThru : public ISeekingPassThru, public CUnknown
  2. {
  3. public:
  4. static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
  5. CSeekingPassThru(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
  6. ~CSeekingPassThru();
  7. DECLARE_IUNKNOWN;
  8. STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
  9. STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);
  10. private:
  11. CPosPassThru              *m_pPosPassThru;
  12. };
  1. CSeekingPassThru::CSeekingPassThru( TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr )
  2. : CUnknown(pName, pUnk, phr),
  3. m_pPosPassThru(NULL)
  4. {
  5. }
3、 CBaseFilter

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

作为Filter的基本特征,CBaseFilter实现了IBaseFilter接口(IbaseFilter从IMediaFilter继承而来)。

同时CBaseFilter还实现了Filter框架(描述了各个Pin组件的情况)。

Filter组件开发中的SDK基类分析

  1. class AM_NOVTABLE CBaseFilter : public CUnknown,        // Handles an IUnknown
  2. public IBaseFilter,     // The Filter Interface
  3. public IAMovieSetup     // For un/registration
  4. {
  5. friend class CBasePin;
  6. protected:
  7. FILTER_STATE    m_State;            // current state: running, paused
  8. IReferenceClock *m_pClock;          // this graph's ref clock
  9. CRefTime        m_tStart;           // offset from stream time to reference time
  10. CLSID       m_clsid;            // This filters clsid
  11. // used for serialization
  12. CCritSec        *m_pLock;           // Object we use for locking
  13. WCHAR           *m_pName;           // Full filter name
  14. IFilterGraph    *m_pGraph;          // Graph we belong to
  15. IMediaEventSink *m_pSink;           // Called with notify events
  16. LONG            m_PinVersion;       // Current pin version
  17. public:
  18. CBaseFilter(
  19. const TCHAR *pName,     // Object description
  20. LPUNKNOWN pUnk,         // IUnknown of delegating object
  21. CCritSec  *pLock,       // Object who maintains lock
  22. REFCLSID   clsid);      // The clsid to be used to serialize this filter
  23. CBaseFilter(
  24. TCHAR     *pName,       // Object description
  25. LPUNKNOWN pUnk,         // IUnknown of delegating object
  26. CCritSec  *pLock,       // Object who maintains lock
  27. REFCLSID   clsid,       // The clsid to be used to serialize this filter
  28. HRESULT   *phr);        // General OLE return code
  29. #ifdef UNICODE
  30. CBaseFilter(
  31. const CHAR *pName,     // Object description
  32. LPUNKNOWN pUnk,         // IUnknown of delegating object
  33. CCritSec  *pLock,       // Object who maintains lock
  34. REFCLSID   clsid);      // The clsid to be used to serialize this filter
  35. CBaseFilter(
  36. CHAR     *pName,       // Object description
  37. LPUNKNOWN pUnk,         // IUnknown of delegating object
  38. CCritSec  *pLock,       // Object who maintains lock
  39. REFCLSID   clsid,       // The clsid to be used to serialize this filter
  40. HRESULT   *phr);        // General OLE return code
  41. #endif
  42. ~CBaseFilter();
  43. DECLARE_IUNKNOWN
  44. // override this to say what interfaces we support where
  45. STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
  46. #ifdef DEBUG
  47. STDMETHODIMP_(ULONG) NonDelegatingRelease();
  48. #endif
  49. //
  50. // --- IPersist method ---
  51. //
  52. STDMETHODIMP GetClassID(CLSID *pClsID);
  53. // --- IMediaFilter methods ---
  54. STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);
  55. STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
  56. STDMETHODIMP GetSyncSource(IReferenceClock **pClock);
  57. // override Stop and Pause so we can activate the pins.
  58. // Note that Run will call Pause first if activation needed.
  59. // Override these if you want to activate your filter rather than
  60. // your pins.
  61. STDMETHODIMP Stop();
  62. STDMETHODIMP Pause();
  63. // the start parameter is the difference to be added to the
  64. // sample's stream time to get the reference time for
  65. // its presentation
  66. STDMETHODIMP Run(REFERENCE_TIME tStart);
  67. // --- helper methods ---
  68. // return the current stream time - ie find out what
  69. // stream time should be appearing now
  70. virtual HRESULT StreamTime(CRefTime& rtStream);
  71. // Is the filter currently active?
  72. BOOL IsActive() {
  73. CAutoLock cObjectLock(m_pLock);
  74. return ((m_State == State_Paused) || (m_State == State_Running));
  75. };
  76. // Is this filter stopped (without locking)
  77. BOOL IsStopped() {
  78. return (m_State == State_Stopped);
  79. };
  80. //
  81. // --- IBaseFilter methods ---
  82. //
  83. // pin enumerator
  84. STDMETHODIMP EnumPins(
  85. IEnumPins ** ppEnum);
  86. // default behaviour of FindPin assumes pin ids are their names
  87. STDMETHODIMP FindPin(
  88. LPCWSTR Id,
  89. IPin ** ppPin
  90. );
  91. STDMETHODIMP QueryFilterInfo(
  92. FILTER_INFO * pInfo);
  93. STDMETHODIMP JoinFilterGraph(
  94. IFilterGraph * pGraph,
  95. LPCWSTR pName);
  96. // return a Vendor information string. Optional - may return E_NOTIMPL.
  97. // memory returned should be freed using CoTaskMemFree
  98. // default implementation returns E_NOTIMPL
  99. STDMETHODIMP QueryVendorInfo(
  100. LPWSTR* pVendorInfo
  101. );
  102. // --- helper methods ---
  103. // send an event notification to the filter graph if we know about it.
  104. // returns S_OK if delivered, S_FALSE if the filter graph does not sink
  105. // events, or an error otherwise.
  106. HRESULT NotifyEvent(
  107. long EventCode,
  108. LONG_PTR EventParam1,
  109. LONG_PTR EventParam2);
  110. // return the filter graph we belong to
  111. IFilterGraph *GetFilterGraph() {
  112. return m_pGraph;
  113. }
  114. // Request reconnect
  115. // pPin is the pin to reconnect
  116. // pmt is the type to reconnect with - can be NULL
  117. // Calls ReconnectEx on the filter graph
  118. HRESULT ReconnectPin(IPin *pPin, AM_MEDIA_TYPE const *pmt);
  119. // find out the current pin version (used by enumerators)
  120. virtual LONG GetPinVersion();
  121. void IncrementPinVersion();
  122. // you need to supply these to access the pins from the enumerator
  123. // and for default Stop and Pause/Run activation.
  124. virtual int GetPinCount() PURE;
  125. virtual CBasePin *GetPin(int n) PURE;
  126. // --- IAMovieSetup methods ---
  127. STDMETHODIMP Register();    // ask filter to register itself
  128. STDMETHODIMP Unregister();  // and unregister itself
  129. // --- setup helper methods ---
  130. // (override to return filters setup data)
  131. virtual LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; }
  132. };

CBaseFilter类的使用方法如下:

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

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

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

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

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

eg:

  1. //
  2. // The filter object itself. Supports IBaseFilter through
  3. // CBaseFilter and also IFileSourceFilter directly in this object
  4. // CAsyncReader类实现了一个Filter,它从CBaseFilter派生,实现了仅含一个输出
  5. // Pin(CAsyncOutputPin类的实例)的Source filter框架。
  6. class CAsyncReader : public CBaseFilter
  7. {
  8. protected:
  9. // filter-wide lock
  10. CCritSec m_csFilter;
  11. // all i/o done here
  12. CAsyncIo m_Io;
  13. // (2)在新类中定义Filter上的Pin的实例(Pin从CBasePin类继承而来);
  14. // our output pin
  15. CAsyncOutputPin m_OutputPin;
  16. // Type we think our data is
  17. CMediaType m_mt;
  18. public:
  19. // construction / destruction
  20. CAsyncReader(
  21. TCHAR *pName,
  22. LPUNKNOWN pUnk,
  23. CAsyncStream *pStream, // 它是Filter获取数据的源
  24. HRESULT *phr);
  25. ~CAsyncReader();
  26. //(3) 实现纯虚函数CBaseFilter::GetPin,用于返回Filter上各个Pin的对象指针;
  27. //(4) 实现纯虚函数CBaseFilter::GetPinCount,用于返回Filter上Pin 的数量;
  28. int GetPinCount();
  29. CBasePin *GetPin(int n);
  30. // --- Access our media type
  31. const CMediaType *LoadType() const
  32. {
  33. return &m_mt;
  34. }
  35. virtual HRESULT Connect(
  36. IPin * pReceivePin,
  37. const AM_MEDIA_TYPE *pmt   // optional media type
  38. )
  39. {
  40. return m_OutputPin.CBasePin::Connect(pReceivePin, pmt);
  41. }
  42. };

还有SDK类的CSource、CBaseRenderer、 CTracsformFilter都是从CBaseFilter继承来的,实现开发Filter时,

使用这些子类作为Filter类。

4、CBasePin

Filter  上最基本的Pin由CBasePin类(参见 amfilter.h)实现。

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

另外,这个类还实现了IQualityControl接口,该接口用于质量控制。

  1. class  AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl
  2. {
  3. protected:
  4. WCHAR *         m_pName;                // This pin's name
  5. IPin            *m_Connected;               // Pin we have connected to
  6. PIN_DIRECTION   m_dir;                      // Direction of this pin
  7. CCritSec        *m_pLock;                   // Object we use for locking
  8. bool            m_bRunTimeError;            // Run time error generated
  9. bool            m_bCanReconnectWhenActive;  // OK to reconnect when active
  10. bool            m_bTryMyTypesFirst;         // When connecting enumerate
  11. // this pin's types first
  12. CBaseFilter    *m_pFilter;                  // Filter we were created by
  13. IQualityControl *m_pQSink;                  // Target for Quality messages
  14. LONG            m_TypeVersion;              // Holds current type version
  15. CMediaType      m_mt;                       // Media type of connection
  16. CRefTime        m_tStart;                   // time from NewSegment call
  17. CRefTime        m_tStop;                    // time from NewSegment
  18. double          m_dRate;                    // rate from NewSegment
  19. #ifdef DEBUG
  20. LONG            m_cRef;                     // Ref count tracing
  21. #endif
  22. // displays pin connection information
  23. #ifdef DEBUG
  24. void DisplayPinInfo(IPin *pReceivePin);
  25. void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt);
  26. #else
  27. void DisplayPinInfo(IPin *pReceivePin) {};
  28. void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {};
  29. #endif
  30. // used to agree a media type for a pin connection
  31. // given a specific media type, attempt a connection (includes
  32. // checking that the type is acceptable to this pin)
  33. HRESULT
  34. AttemptConnection(
  35. IPin* pReceivePin,      // connect to this pin
  36. const CMediaType* pmt   // using this type
  37. );
  38. // try all the media types in this enumerator - for each that
  39. // we accept, try to connect using ReceiveConnection.
  40. HRESULT TryMediaTypes(
  41. IPin *pReceivePin,      // connect to this pin
  42. const CMediaType *pmt,        // proposed type from Connect
  43. IEnumMediaTypes *pEnum);    // try this enumerator
  44. // establish a connection with a suitable mediatype. Needs to
  45. // propose a media type if the pmt pointer is null or partially
  46. // specified - use TryMediaTypes on both our and then the other pin's
  47. // enumerator until we find one that works.
  48. HRESULT AgreeMediaType(
  49. IPin *pReceivePin,      // connect to this pin
  50. const CMediaType *pmt);       // proposed type from Connect
  51. public:
  52. CBasePin(
  53. TCHAR *pObjectName,         // Object description
  54. CBaseFilter *pFilter,       // Owning filter who knows about pins
  55. CCritSec *pLock,            // Object who implements the lock
  56. HRESULT *phr,               // General OLE return code
  57. LPCWSTR pName,              // Pin name for us
  58. PIN_DIRECTION dir);         // Either PINDIR_INPUT or PINDIR_OUTPUT
  59. #ifdef UNICODE
  60. CBasePin(
  61. CHAR *pObjectName,         // Object description
  62. CBaseFilter *pFilter,       // Owning filter who knows about pins
  63. CCritSec *pLock,            // Object who implements the lock
  64. HRESULT *phr,               // General OLE return code
  65. LPCWSTR pName,              // Pin name for us
  66. PIN_DIRECTION dir);         // Either PINDIR_INPUT or PINDIR_OUTPUT
  67. #endif
  68. virtual ~CBasePin();
  69. DECLARE_IUNKNOWN
  70. STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
  71. STDMETHODIMP_(ULONG) NonDelegatingRelease();
  72. STDMETHODIMP_(ULONG) NonDelegatingAddRef();
  73. // --- IPin methods ---
  74. // take lead role in establishing a connection. Media type pointer
  75. // may be null, or may point to partially-specified mediatype
  76. // (subtype or format type may be GUID_NULL).
  77. STDMETHODIMP Connect(
  78. IPin * pReceivePin,
  79. const AM_MEDIA_TYPE *pmt   // optional media type
  80. );
  81. // (passive) accept a connection from another pin
  82. STDMETHODIMP ReceiveConnection(
  83. IPin * pConnector,      // this is the initiating connecting pin
  84. const AM_MEDIA_TYPE *pmt   // this is the media type we will exchange
  85. );
  86. STDMETHODIMP Disconnect();
  87. STDMETHODIMP ConnectedTo(IPin **pPin);
  88. STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *pmt);
  89. STDMETHODIMP QueryPinInfo(
  90. PIN_INFO * pInfo
  91. );
  92. STDMETHODIMP QueryDirection(
  93. PIN_DIRECTION * pPinDir
  94. );
  95. STDMETHODIMP QueryId(
  96. LPWSTR * Id
  97. );
  98. // does the pin support this media type
  99. STDMETHODIMP QueryAccept(
  100. const AM_MEDIA_TYPE *pmt
  101. );
  102. // return an enumerator for this pins preferred media types
  103. STDMETHODIMP EnumMediaTypes(
  104. IEnumMediaTypes **ppEnum
  105. );
  106. // return an array of IPin* - the pins that this pin internally connects to
  107. // All pins put in the array must be AddReffed (but no others)
  108. // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE
  109. // Default: return E_NOTIMPL
  110. // The filter graph will interpret NOT_IMPL as any input pin connects to
  111. // all visible output pins and vice versa.
  112. // apPin can be NULL if nPin==0 (not otherwise).
  113. STDMETHODIMP QueryInternalConnections(
  114. IPin* *apPin,     // array of IPin*
  115. ULONG *nPin       // on input, the number of slots
  116. // on output  the number of pins
  117. ) { return E_NOTIMPL; }
  118. // Called when no more data will be sent
  119. STDMETHODIMP EndOfStream(void);
  120. // Begin/EndFlush still PURE
  121. // NewSegment notifies of the start/stop/rate applying to the data
  122. // about to be received. Default implementation records data and
  123. // returns S_OK.
  124. // Override this to pass downstream.
  125. STDMETHODIMP NewSegment(
  126. REFERENCE_TIME tStart,
  127. REFERENCE_TIME tStop,
  128. double dRate);
  129. //================================================================================
  130. // IQualityControl methods
  131. //================================================================================
  132. STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
  133. STDMETHODIMP SetSink(IQualityControl * piqc);
  134. // --- helper methods ---
  135. // Returns true if the pin is connected. false otherwise.
  136. BOOL IsConnected(void) {return (m_Connected != NULL); };
  137. // Return the pin this is connected to (if any)
  138. IPin * GetConnected() { return m_Connected; };
  139. // Check if our filter is currently stopped
  140. BOOL IsStopped() {
  141. return (m_pFilter->m_State == State_Stopped);
  142. };
  143. // find out the current type version (used by enumerators)
  144. virtual LONG GetMediaTypeVersion();
  145. void IncrementTypeVersion();
  146. // switch the pin to active (paused or running) mode
  147. // not an error to call this if already active
  148. virtual HRESULT Active(void);
  149. // switch the pin to inactive state - may already be inactive
  150. virtual HRESULT Inactive(void);
  151. // Notify of Run() from filter
  152. virtual HRESULT Run(REFERENCE_TIME tStart);
  153. // check if the pin can support this specific proposed type and format
  154. virtual HRESULT CheckMediaType(const CMediaType *) PURE;
  155. // set the connection to use this format (previously agreed)
  156. virtual HRESULT SetMediaType(const CMediaType *);
  157. // check that the connection is ok before verifying it
  158. // can be overridden eg to check what interfaces will be supported.
  159. virtual HRESULT CheckConnect(IPin *);
  160. // Set and release resources required for a connection
  161. virtual HRESULT BreakConnect();
  162. virtual HRESULT CompleteConnect(IPin *pReceivePin);
  163. // returns the preferred formats for a pin
  164. virtual HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
  165. // access to NewSegment values
  166. REFERENCE_TIME CurrentStopTime() {
  167. return m_tStop;
  168. }
  169. REFERENCE_TIME CurrentStartTime() {
  170. return m_tStart;
  171. }
  172. double CurrentRate() {
  173. return m_dRate;
  174. }
  175. //  Access name
  176. LPWSTR Name() { return m_pName; };
  177. //  Can reconnectwhen active?
  178. void SetReconnectWhenActive(bool bCanReconnect)
  179. {
  180. m_bCanReconnectWhenActive = bCanReconnect;
  181. }
  182. bool CanReconnectWhenActive()
  183. {
  184. return m_bCanReconnectWhenActive;
  185. }
  186. protected:
  187. STDMETHODIMP DisconnectInternal();
  188. };

在CBasePin实现的成员函数中,有3个与Filter的状态转换相对应。

  1. // 转换pin到活动状态(暂停或运行)
  2. // not an error to call this if already active
  3. virtual HRESULT Active(void);
  4. // 转换pin到不活动状态 - 可能已经处于非活动状态
  5. virtual HRESULT Inactive(void);
  6. // 通知运行filter 的 Run()
  7. virtual HRESULT Run(REFERENCE_TIME tStart);

我们来看一下Filter的Stop的实现,实际上就是调用Filter的所有pin的Inactive函数

  1. STDMETHODIMP
  2. CBaseFilter::Stop()
  3. {
  4. CAutoLock cObjectLock(m_pLock);
  5. HRESULT hr = NOERROR;
  6. // 通知所有pin改变状态
  7. if (m_State != State_Stopped) {
  8. int cPins = GetPinCount();
  9. for (int c = 0; c < cPins; c++) {
  10. CBasePin *pPin = GetPin(c);
  11. // Disconnected pins are not activated - this saves pins worrying
  12. // about this state themselves. We ignore the return code to make
  13. // sure everyone is inactivated regardless. The base input pin
  14. // class can return an error if it has no allocator but Stop can
  15. // be used to resync the graph state after something has gone bad
  16. // 仅在完成连接的pin上调用Inactive函数
  17. // 如果Inactive函数返回一个错误值,则暂时忽略,
  18. // 以便所有Pin都有机会被调用Inactive
  19. if (pPin->IsConnected()) {
  20. HRESULT hrTmp = pPin->Inactive();
  21. if (FAILED(hrTmp) && SUCCEEDED(hr)) {
  22. hr = hrTmp;
  23. }
  24. }
  25. }
  26. }
  27. m_State = State_Stopped;
  28. return hr;
  29. }

在实际开发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:

  1. // CAsyncOutputPin实现了一个输出Pin
  2. // 继承自IAsyncReader、CBasePin,这是对拉模式的Source Filter的基本要求
  3. /* IAsyncReader接口方法及描述如下:
  4. BeginFlush  放弃所有正在进行的数据读取
  5. EndFlush    与BeginFlush配对,标示Flush过程结束
  6. Length      得到数据总长度和当前可以读取的长度
  7. RequestAlloctor 要求一个输入Pin上的Sample管理器
  8. Request     发出一个数据请求
  9. SyncReadAligned 同步读取数据(边界对齐)
  10. SyncRead    同步读取数据
  11. WaitForNext 等待一个请求的完成
  12. ======================================================================
  13. 可以看出CAsyOutputPin类上实现的IAsyncReader的各个接口方法,都“委托”
  14. 给了CAsyncIo类对象的同名成员函数
  15. */
  16. class CAsyncOutputPin
  17. : public IAsyncReader,
  18. public CBasePin
  19. {
  20. protected:
  21. CAsyncReader* m_pReader;
  22. CAsyncIo * m_pIo;
  23. //  This is set every time we're asked to return an IAsyncReader
  24. //  interface
  25. //  This allows us to know if the downstream pin can use
  26. //  this transport, otherwise we can hook up to thinks like the
  27. //  dump filter and nothing happens
  28. BOOL         m_bQueriedForAsyncReader;
  29. HRESULT InitAllocator(IMemAllocator **ppAlloc);
  30. public:
  31. // constructor and destructor
  32. CAsyncOutputPin(
  33. HRESULT * phr,
  34. CAsyncReader *pReader,
  35. CAsyncIo *pIo,
  36. CCritSec * pLock);
  37. ~CAsyncOutputPin();
  38. // --- CUnknown ---
  39. // need to expose IAsyncReader
  40. DECLARE_IUNKNOWN
  41. STDMETHODIMP NonDelegatingQueryInterface(REFIID, void**);
  42. // --- IPin methods ---
  43. STDMETHODIMP Connect(
  44. IPin * pReceivePin,
  45. const AM_MEDIA_TYPE *pmt   // optional media type
  46. );
  47. // --- CBasePin methods ---
  48. // return the types we prefer - this will return the known
  49. // file type
  50. HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
  51. // can we support this type?
  52. HRESULT CheckMediaType(const CMediaType* pType);
  53. // Clear the flag so we see if IAsyncReader is queried for
  54. HRESULT CheckConnect(IPin *pPin)
  55. {
  56. m_bQueriedForAsyncReader = FALSE;
  57. return CBasePin::CheckConnect(pPin);
  58. }
  59. // See if it was asked for
  60. HRESULT CompleteConnect(IPin *pReceivePin)
  61. {
  62. if (m_bQueriedForAsyncReader) {
  63. return CBasePin::CompleteConnect(pReceivePin);
  64. } else {
  65. #ifdef VFW_E_NO_TRANSPORT
  66. return VFW_E_NO_TRANSPORT;
  67. #else
  68. return E_FAIL;
  69. #endif
  70. }
  71. }
  72. //  Remove our connection status
  73. HRESULT BreakConnect()
  74. {
  75. m_bQueriedForAsyncReader = FALSE;
  76. return CBasePin::BreakConnect();
  77. }
  78. // --- IAsyncReader methods ---
  79. // pass in your preferred allocator and your preferred properties.
  80. // method returns the actual allocator to be used. Call GetProperties
  81. // on returned allocator to learn alignment and prefix etc chosen.
  82. // this allocator will be not be committed and decommitted by
  83. // the async reader, only by the consumer.
  84. STDMETHODIMP RequestAllocator(
  85. IMemAllocator* pPreferred,
  86. ALLOCATOR_PROPERTIES* pProps,
  87. IMemAllocator ** ppActual);
  88. // queue a request for data.
  89. // media sample start and stop times contain the requested absolute
  90. // byte position (start inclusive, stop exclusive).
  91. // may fail if sample not obtained from agreed allocator.
  92. // may fail if start/stop position does not match agreed alignment.
  93. // samples allocated from source pin's allocator may fail
  94. // GetPointer until after returning from WaitForNext.
  95. STDMETHODIMP Request(
  96. IMediaSample* pSample,
  97. DWORD_PTR dwUser);         // user context
  98. // block until the next sample is completed or the timeout occurs.
  99. // timeout (millisecs) may be 0 or INFINITE. Samples may not
  100. // be delivered in order. If there is a read error of any sort, a
  101. // notification will already have been sent by the source filter,
  102. // and STDMETHODIMP will be an error.
  103. STDMETHODIMP WaitForNext(
  104. DWORD dwTimeout,
  105. IMediaSample** ppSample,  // completed sample
  106. DWORD_PTR * pdwUser);     // user context
  107. // sync read of data. Sample passed in must have been acquired from
  108. // the agreed allocator. Start and stop position must be aligned.
  109. // equivalent to a Request/WaitForNext pair, but may avoid the
  110. // need for a thread on the source filter.
  111. STDMETHODIMP SyncReadAligned(
  112. IMediaSample* pSample);
  113. // sync read. works in stopped state as well as run state.
  114. // need not be aligned. Will fail if read is beyond actual total
  115. // length.
  116. STDMETHODIMP SyncRead(
  117. LONGLONG llPosition,  // absolute file position
  118. LONG lLength,         // nr bytes required
  119. BYTE* pBuffer);       // write data here
  120. // return total length of stream, and currently available length.
  121. // reads for beyond the available length but within the total length will
  122. // normally succeed but may block for a long period.
  123. STDMETHODIMP Length(
  124. LONGLONG* pTotal,
  125. LONGLONG* pAvailable);
  126. // cause all outstanding reads to return, possibly with a failure code
  127. // (VFW_E_TIMEOUT) indicating they were cancelled.
  128. // these are defined on IAsyncReader and IPin
  129. STDMETHODIMP BeginFlush(void);
  130. STDMETHODIMP EndFlush(void);
  131. };
5、 CBaseInputPin和CBaseOutputPin

从CBasePin类派生的,也是很基本的输入或输出pin。

它们的实现可参见 amfilter.cpp

CBaseInputPin实现了IMemInputPin(用于推模式的数据传送),

而CBaseOutputPin主要完成了传送数据所使用的Sample管理器(Allocator)的协商,并重写了CBasePin::Active(用于实际的Sample内存分配)

以及CBasePin::Inactive(用于Sample内存的释放)

Filter组件开发中的SDK基类分析

  1. class AM_NOVTABLE CBaseInputPin : public CBasePin,
  2. public IMemInputPin
  3. {
  4. protected:
  5. IMemAllocator *m_pAllocator;    // Default memory allocator
  6. // allocator is read-only, so received samples
  7. // cannot be modified (probably only relevant to in-place
  8. // transforms
  9. BYTE m_bReadOnly;
  10. // in flushing state (between BeginFlush and EndFlush)
  11. // if TRUE, all Receives are returned with S_FALSE
  12. BYTE m_bFlushing;
  13. // Sample properties - initalized in Receive
  14. AM_SAMPLE2_PROPERTIES m_SampleProps;
  15. public:
  16. CBaseInputPin(
  17. TCHAR *pObjectName,
  18. CBaseFilter *pFilter,
  19. CCritSec *pLock,
  20. HRESULT *phr,
  21. LPCWSTR pName);
  22. #ifdef UNICODE
  23. CBaseInputPin(
  24. CHAR *pObjectName,
  25. CBaseFilter *pFilter,
  26. CCritSec *pLock,
  27. HRESULT *phr,
  28. LPCWSTR pName);
  29. #endif
  30. virtual ~CBaseInputPin();
  31. DECLARE_IUNKNOWN
  32. // override this to publicise our interfaces
  33. STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
  34. // return the allocator interface that this input pin
  35. // would like the output pin to use
  36. STDMETHODIMP GetAllocator(IMemAllocator ** ppAllocator);
  37. // tell the input pin which allocator the output pin is actually
  38. // going to use.
  39. STDMETHODIMP NotifyAllocator(
  40. IMemAllocator * pAllocator,
  41. BOOL bReadOnly);
  42. // do something with this media sample
  43. STDMETHODIMP Receive(IMediaSample *pSample);
  44. // do something with these media samples
  45. STDMETHODIMP ReceiveMultiple (
  46. IMediaSample **pSamples,
  47. long nSamples,
  48. long *nSamplesProcessed);
  49. // See if Receive() blocks
  50. STDMETHODIMP ReceiveCanBlock();
  51. // Default handling for BeginFlush - call at the beginning
  52. // of your implementation (makes sure that all Receive calls
  53. // fail). After calling this, you need to free any queued data
  54. // and then call downstream.
  55. STDMETHODIMP BeginFlush(void);
  56. // default handling for EndFlush - call at end of your implementation
  57. // - before calling this, ensure that there is no queued data and no thread
  58. // pushing any more without a further receive, then call downstream,
  59. // then call this method to clear the m_bFlushing flag and re-enable
  60. // receives
  61. STDMETHODIMP EndFlush(void);
  62. // this method is optional (can return E_NOTIMPL).
  63. // default implementation returns E_NOTIMPL. Override if you have
  64. // specific alignment or prefix needs, but could use an upstream
  65. // allocator
  66. STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES*pProps);
  67. // Release the pin's allocator.
  68. HRESULT BreakConnect();
  69. // helper method to check the read-only flag
  70. BOOL IsReadOnly() {
  71. return m_bReadOnly;
  72. };
  73. // helper method to see if we are flushing
  74. BOOL IsFlushing() {
  75. return m_bFlushing;
  76. };
  77. //  Override this for checking whether it's OK to process samples
  78. //  Also call this from EndOfStream.
  79. virtual HRESULT CheckStreaming();
  80. // Pass a Quality notification on to the appropriate sink
  81. HRESULT PassNotify(Quality& q);
  82. //================================================================================
  83. // IQualityControl methods (from CBasePin)
  84. //================================================================================
  85. STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
  86. // no need to override:
  87. // STDMETHODIMP SetSink(IQualityControl * piqc);
  88. // switch the pin to inactive state - may already be inactive
  89. virtual HRESULT Inactive(void);
  90. // Return sample properties pointer
  91. AM_SAMPLE2_PROPERTIES * SampleProps() {
  92. ASSERT(m_SampleProps.cbData != 0);
  93. return &m_SampleProps;
  94. }
  95. };
  1. class  AM_NOVTABLE CBaseOutputPin : public CBasePin
  2. {
  3. protected:
  4. IMemAllocator *m_pAllocator;
  5. IMemInputPin *m_pInputPin;        // interface on the downstreaminput pin
  6. // set up in CheckConnect when we connect.
  7. public:
  8. CBaseOutputPin(
  9. TCHAR *pObjectName,
  10. CBaseFilter *pFilter,
  11. CCritSec *pLock,
  12. HRESULT *phr,
  13. LPCWSTR pName);
  14. #ifdef UNICODE
  15. CBaseOutputPin(
  16. CHAR *pObjectName,
  17. CBaseFilter *pFilter,
  18. CCritSec *pLock,
  19. HRESULT *phr,
  20. LPCWSTR pName);
  21. #endif
  22. // override CompleteConnect() so we can negotiate an allocator
  23. virtual HRESULT CompleteConnect(IPin *pReceivePin);
  24. // negotiate the allocator and its buffer size/count and other properties
  25. // Calls DecideBufferSize to set properties
  26. virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc);
  27. // override this to set the buffer size and count. Return an error
  28. // if the size/count is not to your liking.
  29. // The allocator properties passed in are those requested by the
  30. // input pin - use eg the alignment and prefix members if you have
  31. // no preference on these.
  32. virtual HRESULT DecideBufferSize(
  33. IMemAllocator * pAlloc,
  34. ALLOCATOR_PROPERTIES * ppropInputRequest
  35. ) PURE;
  36. // returns an empty sample buffer from the allocator
  37. virtual HRESULT GetDeliveryBuffer(IMediaSample ** ppSample,
  38. REFERENCE_TIME * pStartTime,
  39. REFERENCE_TIME * pEndTime,
  40. DWORD dwFlags);
  41. // deliver a filled-in sample to the connected input pin
  42. // note - you need to release it after calling this. The receiving
  43. // pin will addref the sample if it needs to hold it beyond the
  44. // call.
  45. virtual HRESULT Deliver(IMediaSample *);
  46. // override this to control the connection
  47. virtual HRESULT InitAllocator(IMemAllocator **ppAlloc);
  48. HRESULT CheckConnect(IPin *pPin);
  49. HRESULT BreakConnect();
  50. // override to call Commit and Decommit
  51. HRESULT Active(void);
  52. HRESULT Inactive(void);
  53. // we have a default handling of EndOfStream which is to return
  54. // an error, since this should be called on input pins only
  55. STDMETHODIMP EndOfStream(void);
  56. // called from elsewhere in our filter to pass EOS downstream to
  57. // our connected input pin
  58. virtual HRESULT DeliverEndOfStream(void);
  59. // same for Begin/EndFlush - we handle Begin/EndFlush since it
  60. // is an error on an output pin, and we have Deliver methods to
  61. // call the methods on the connected pin
  62. STDMETHODIMP BeginFlush(void);
  63. STDMETHODIMP EndFlush(void);
  64. virtual HRESULT DeliverBeginFlush(void);
  65. virtual HRESULT DeliverEndFlush(void);
  66. // deliver NewSegment to connected pin - you will need to
  67. // override this if you queue any data in your output pin.
  68. virtual HRESULT DeliverNewSegment(
  69. REFERENCE_TIME tStart,
  70. REFERENCE_TIME tStop,
  71. double dRate);
  72. //================================================================================
  73. // IQualityControl methods
  74. //================================================================================
  75. // All inherited from CBasePin and not overridden here.
  76. // STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
  77. // STDMETHODIMP SetSink(IQualityControl * piqc);
  78. };

CBaseInputPin类的使用方法(派生一个子类,并且至少需要重写以下函数)如下:

(1) CBaseInputPin::BeginFlush

(2) CBaseInputPin::EndFlush

(3) CBaseInputPin::Receive

(4) CBaseInputPin::CheckMediaType

(5) CBaseInputPin::GetMediaType

eg:

  1. class CRendererInputPin : public CBaseInputPin
  2. {
  3. protected:
  4. CBaseRenderer *m_pRenderer;
  5. public:
  6. CRendererInputPin(CBaseRenderer *pRenderer,
  7. HRESULT *phr,
  8. LPCWSTR Name);
  9. // Overriden from the base pin classes
  10. HRESULT BreakConnect();
  11. HRESULT CompleteConnect(IPin *pReceivePin);
  12. HRESULT SetMediaType(const CMediaType *pmt);
  13. HRESULT CheckMediaType(const CMediaType *pmt);
  14. HRESULT Active();
  15. HRESULT Inactive();
  16. // Add rendering behaviour to interface functions
  17. STDMETHODIMP QueryId(LPWSTR *Id);
  18. STDMETHODIMP EndOfStream();
  19. STDMETHODIMP BeginFlush();
  20. STDMETHODIMP EndFlush();
  21. STDMETHODIMP Receive(IMediaSample *pMediaSample);
  22. // Helper
  23. IMemAllocator inline *Allocator() const
  24. {
  25. return m_pAllocator;
  26. }
  27. };

CBaseOutputPin类的使用方法(派生一个子类,并且最少需要重写以下函数)如下:

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

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

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

  1. class CTransformOutputPin : public CBaseOutputPin
  2. {
  3. friend class CTransformFilter;
  4. protected:
  5. CTransformFilter *m_pTransformFilter;
  6. public:
  7. // implement IMediaPosition by passing upstream
  8. IUnknown * m_pPosition;
  9. CTransformOutputPin(
  10. TCHAR *pObjectName,
  11. CTransformFilter *pTransformFilter,
  12. HRESULT * phr,
  13. LPCWSTR pName);
  14. #ifdef UNICODE
  15. CTransformOutputPin(
  16. CHAR *pObjectName,
  17. CTransformFilter *pTransformFilter,
  18. HRESULT * phr,
  19. LPCWSTR pName);
  20. #endif
  21. ~CTransformOutputPin();
  22. // override to expose IMediaPosition
  23. STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
  24. // --- CBaseOutputPin ------------
  25. STDMETHODIMP QueryId(LPWSTR * Id)
  26. {
  27. return AMGetWideString(L"Out", Id);
  28. }
  29. // Grab and release extra interfaces if required
  30. HRESULT CheckConnect(IPin *pPin);
  31. HRESULT BreakConnect();
  32. HRESULT CompleteConnect(IPin *pReceivePin);
  33. // check that we can support this output type
  34. HRESULT CheckMediaType(const CMediaType* mtOut);
  35. // set the connection media type
  36. HRESULT SetMediaType(const CMediaType *pmt);
  37. // called from CBaseOutputPin during connection to ask for
  38. // the count and size of buffers we need.
  39. HRESULT DecideBufferSize(
  40. IMemAllocator * pAlloc,
  41. ALLOCATOR_PROPERTIES *pProp);
  42. // returns the preferred formats for a pin
  43. HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
  44. // inherited from IQualityControl via CBasePin
  45. STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
  46. // Media type
  47. public:
  48. CMediaType& CurrentMediaType() { return m_mt; };
  49. };

===================================================================

如果开发的是一个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等。

它们的继承关系如图:

Filter组件开发中的SDK基类分析

如上图所示,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: 参照我的另一篇文章:

  1. class CPushSourceBitmap : public CSource
  2. {
  3. private:
  4. // Constructor is private because you have to use CreateInstance
  5. CPushSourceBitmap(IUnknown *pUnk, HRESULT *phr);
  6. ~CPushSourceBitmap();
  7. CPushPinBitmap *m_pPin;
  8. public:
  9. static CUnknown * WINAPI CreateInstance(IUnknown *pUnk, HRESULT *phr);
  10. };
  1. CPushSourceBitmap::CPushSourceBitmap(IUnknown *pUnk, HRESULT *phr)
  2. : CSource(NAME("PushSourceBitmap"), pUnk, CLSID_PushSourceBitmap)
  3. {
  4. // The pin magically adds itself to our pin array.
  5. m_pPin = new CPushPinBitmap(phr, this);
  6. if (phr)
  7. {
  8. if (m_pPin == NULL)
  9. *phr = E_OUTOFMEMORY;
  10. else
  11. *phr = S_OK;
  12. }
  13. }

7 、  CSourceStream

CSource实际上继承自CBaseFilter,提供了一个“推”数据的能力,这种Filter至少有一个输出

Pin采用了CSourecStream类(或CSourceStream的子类)。如下图所示:

CSourceStream上实现了一个线程(CSourceStream从CAMThread类继承而来),Sample数据就是靠这个线程向一线Filter发送的。

Filter组件开发中的SDK基类分析

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: 参照我的另一篇文章:

  1. class CPushPinBitmap : public CSourceStream
  2. {
  3. protected:
  4. int m_FramesWritten;                // To track where we are in the file
  5. BOOL m_bZeroMemory;                 // Do we need to clear the buffer?
  6. CRefTime m_rtSampleTime;            // The time stamp for each sample
  7. BITMAPINFO *m_pBmi;                 // Pointer to the bitmap header
  8. DWORD       m_cbBitmapInfo;         // Size of the bitmap header
  9. // File opening variables
  10. HANDLE m_hFile;                     // Handle returned from CreateFile
  11. BYTE * m_pFile;                     // Points to beginning of file buffer
  12. BYTE * m_pImage;                    // Points to pixel bits
  13. int m_iFrameNumber;
  14. const REFERENCE_TIME m_rtFrameLength;
  15. CCritSec m_cSharedState;            // Protects our internal state
  16. CImageDisplay m_Display;            // Figures out our media type for us
  17. public:
  18. CPushPinBitmap(HRESULT *phr, CSource *pFilter);
  19. ~CPushPinBitmap();
  20. // Override the version that offers exactly one media type
  21. HRESULT GetMediaType(CMediaType *pMediaType);
  22. HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest);
  23. HRESULT FillBuffer(IMediaSample *pSample);
  24. // Quality control
  25. // Not implemented because we aren't going in real time.
  26. // If the file-writing filter slows the graph down, we just do nothing, which means
  27. // wait until we're unblocked. No frames are ever dropped.
  28. STDMETHODIMP Notify(IBaseFilter *pSelf, Quality q)
  29. {
  30. return E_FAIL;
  31. }
  32. };

8、 CTransformFilter

CTransformFilter类是开发Transform Filter最基本的类,也是最常用到的类。结构如下:

它有一个输入Pin和一个输出Pin,分别使用CTransformInputPin类和CTransformOutputPin类。

Filter组件开发中的SDK基类分析

Filter组件开发中的SDK基类分析

从图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 的继承类去实现它们。

  1. class CVideoTransformFilter : public CTransformFilter
  2. {
  3. public:
  4. CVideoTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid);
  5. ~CVideoTransformFilter();
  6. HRESULT EndFlush();
  7. // =================================================================
  8. // ----- override these bits ---------------------------------------
  9. // =================================================================
  10. // The following methods are in CTransformFilter which is inherited.
  11. // They are mentioned here for completeness
  12. //
  13. // These MUST be supplied in a derived class
  14. //
  15. // NOTE:
  16. // virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
  17. // virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
  18. // virtual HRESULT CheckTransform
  19. //     (const CMediaType* mtIn, const CMediaType* mtOut) PURE;
  20. // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
  21. // virtual HRESULT DecideBufferSize
  22. //     (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE;
  23. // virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
  24. //
  25. // These MAY also be overridden
  26. //
  27. // virtual HRESULT StopStreaming();
  28. // virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
  29. // virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
  30. // virtual HRESULT BreakConnect(PIN_DIRECTION dir);
  31. // virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
  32. // virtual HRESULT EndOfStream(void);
  33. // virtual HRESULT BeginFlush(void);
  34. // virtual HRESULT EndFlush(void);
  35. // virtual HRESULT NewSegment
  36. //     (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate);
  37. #ifdef PERF
  38. // If you override this - ensure that you register all these ids
  39. // as well as any of your own,
  40. virtual void RegisterPerfId() {
  41. m_idSkip        = MSR_REGISTER(TEXT("Video Transform Skip frame"));
  42. m_idFrameType   = MSR_REGISTER(TEXT("Video transform frame type"));
  43. m_idLate        = MSR_REGISTER(TEXT("Video Transform Lateness"));
  44. m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key"));
  45. CTransformFilter::RegisterPerfId();
  46. }
  47. #endif
  48. protected:
  49. // =========== QUALITY MANAGEMENT IMPLEMENTATION ========================
  50. // Frames are assumed to come in three types:
  51. // Type 1: an AVI key frame or an MPEG I frame.
  52. //        This frame can be decoded with no history.
  53. //        Dropping this frame means that no further frame can be decoded
  54. //        until the next type 1 frame.
  55. //        Type 1 frames are sync points.
  56. // Type 2: an AVI non-key frame or an MPEG P frame.
  57. //        This frame cannot be decoded unless the previous type 1 frame was
  58. //        decoded and all type 2 frames since have been decoded.
  59. //        Dropping this frame means that no further frame can be decoded
  60. //        until the next type 1 frame.
  61. // Type 3: An MPEG B frame.
  62. //        This frame cannot be decoded unless the previous type 1 or 2 frame
  63. //        has been decoded AND the subsequent type 1 or 2 frame has also
  64. //        been decoded.  (This requires decoding the frames out of sequence).
  65. //        Dropping this frame affects no other frames.  This implementation
  66. //        does not allow for these.  All non-sync-point frames are treated
  67. //        as being type 2.
  68. //
  69. // The spacing of frames of type 1 in a file is not guaranteed.  There MUST
  70. // be a type 1 frame at (well, near) the start of the file in order to start
  71. // decoding at all.  After that there could be one every half second or so,
  72. // there could be one at the start of each scene (aka "cut", "shot") or
  73. // there could be no more at all.
  74. // If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED
  75. // without losing all the rest of the movie.  There is no way to tell whether
  76. // this is the case, so we find that we are in the gambling business.
  77. // To try to improve the odds, we record the greatest interval between type 1s
  78. // that we have seen and we bet on things being no worse than this in the
  79. // future.
  80. // You can tell if it's a type 1 frame by calling IsSyncPoint().
  81. // there is no architected way to test for a type 3, so you should override
  82. // the quality management here if you have B-frames.
  83. int m_nKeyFramePeriod; // the largest observed interval between type 1 frames
  84. // 1 means every frame is type 1, 2 means every other.
  85. int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1.
  86. // becomes the new m_nKeyFramePeriod if greater.
  87. BOOL m_bSkipping;           // we are skipping to the next type 1 frame
  88. #ifdef PERF
  89. int m_idFrameType;          // MSR id Frame type.  1=Key, 2="non-key"
  90. int m_idSkip;               // MSR id skipping
  91. int m_idLate;               // MSR id lateness
  92. int m_idTimeTillKey;        // MSR id for guessed time till next key frame.
  93. #endif
  94. virtual HRESULT StartStreaming();
  95. HRESULT AbortPlayback(HRESULT hr);  // if something bad happens
  96. HRESULT Receive(IMediaSample *pSample);
  97. HRESULT AlterQuality(Quality q);
  98. BOOL ShouldSkipFrame(IMediaSample * pIn);
  99. int m_itrLate;              // lateness from last Quality message
  100. // (this overflows at 214 secs late).
  101. int m_tDecodeStart;         // timeGetTime when decode started.
  102. int m_itrAvgDecode;         // Average decode time in reference units.
  103. BOOL m_bNoSkip;             // debug - no skipping.
  104. // We send an EC_QUALITY_CHANGE notification to the app if we have to degrade.
  105. // We send one when we start degrading, not one for every frame, this means
  106. // we track whether we've sent one yet.
  107. BOOL m_bQualityChanged;
  108. // When non-zero, don't pass anything to renderer until next keyframe
  109. // If there are few keys, give up and eventually draw something
  110. int m_nWaitForKey;
  111. };

9、 CTransInPlaceFilter

CTransInPlaceFilter是一个“就地”处理的Transform Filter类。结构如下:

与CTransformFilter,CTransInPlaceFilter也有一个输入Pin和一个输出Pin,但使用CTransInPlaceOutputPin类。

Filter组件开发中的SDK基类分析

Filter组件开发中的SDK基类分析

CTransInPlaceFilter的输入和输出Pin上一般使用相同的媒体类型进行连接,并且使用同一个Sample管理器(如果Filter实现时要修改Sample数据,

而协商达成一致的Sample管理器只读的,那么CTransInPlaceFilter的输入和输出Pin将不得不使用各自的Sample管理器)。

CTransInPlaceFilter类要实现上述的目标,主要依赖于CTransInPlaceFilter::CompleteConnect、CTransInPlaceInputPin::GetAllocator和

CTransInPlaceInputPin::NotifyAlocator的函数实现。代码如下:

  1. // 当输入或输出Pin完成连接时被调用,
  2. // 经过一个反复重连的过程,来达到输入和输出Pin使用相同的媒体类型的目的
  3. HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin)
  4. {
  5. UNREFERENCED_PARAMETER(pReceivePin);
  6. ASSERT(m_pInput);
  7. ASSERT(m_pOutput);
  8. // if we are not part of a graph, then don't indirect the pointer
  9. // this probably prevents use of the filter without a filtergraph
  10. if (!m_pGraph) {
  11. return VFW_E_NOT_IN_GRAPH;
  12. }
  13. // Always reconnect the input to account for buffering changes
  14. //
  15. // Because we don't get to suggest a type on ReceiveConnection
  16. // we need another way of making sure the right type gets used.
  17. //
  18. // One way would be to have our EnumMediaTypes return our output
  19. // connection type first but more deterministic and simple is to
  20. // call ReconnectEx passing the type we want to reconnect with
  21. // via the base class ReconeectPin method.
  22. // 当输出Pin调用该函数(并且此时输入Pin已连上)时,使用输出Pin上的媒体类型对
  23. // 输入Pin进行重连接
  24. if (dir == PINDIR_OUTPUT) {
  25. if( m_pInput->IsConnected() ) {
  26. return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() );
  27. }
  28. return NOERROR;
  29. }
  30. ASSERT(dir == PINDIR_INPUT);
  31. // Reconnect output if necessary
  32. // 当输入Pin调用该函数(并且此时输出Pin已连上)时,如果输入和输出Pin上使用的
  33. // 媒体类型不一致,则使用输入Pin上的媒体类型对输出Pin进行重新连接
  34. if( m_pOutput->IsConnected() ) {
  35. if (  m_pInput->CurrentMediaType()
  36. != m_pOutput->CurrentMediaType()
  37. ) {
  38. return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() );
  39. }
  40. }
  41. return NOERROR;
  42. } // ComnpleteConnect
  1. // 当上一级Filter的输出Pin要求我们的输入Pin提供Sample管理器时,
  2. // 如果我们的输出Pin已连接上,则可以取出输出Pin上的Sample管理器提供给上一级
  3. // Filter,以此达到我们的输入和输出Pin使用同一个Sample管理器的目的
  4. STDMETHODIMP CTransInPlaceInputPin::GetAllocator(IMemAllocator ** ppAllocator)
  5. {
  6. CheckPointer(ppAllocator,E_POINTER);
  7. ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
  8. CAutoLock cObjectLock(m_pLock);
  9. HRESULT hr;
  10. if ( m_pTIPFilter->m_pOutput->IsConnected() ) {
  11. //  Store the allocator we got
  12. hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
  13. ->GetAllocator( ppAllocator );
  14. if (SUCCEEDED(hr)) {
  15. m_pTIPFilter->OutputPin()->SetAllocator( *ppAllocator );
  16. }
  17. }
  18. else {
  19. //  Help upstream filter (eg TIP filter which is having to do a copy)
  20. //  by providing a temp allocator here - we'll never use
  21. //  this allocator because when our output is connected we'll
  22. //  reconnect this pin
  23. hr = CTransformInputPin::GetAllocator( ppAllocator );
  24. }
  25. return hr;
  26. } // GetAllocator
  1. // 上一级Filter调用该函数,告知输入Pin上到底使用哪一个Sample管理器
  2. // 如果设置进来的Sample管理器是只读的,而我们在Filter中又想修改数据,
  3. // 则我们的Filter不得不最终使用不同的Sample管理器
  4. STDMETHODIMP
  5. CTransInPlaceInputPin::NotifyAllocator(
  6. IMemAllocator * pAllocator,
  7. BOOL bReadOnly)
  8. {
  9. HRESULT hr = S_OK;
  10. CheckPointer(pAllocator,E_POINTER);
  11. ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
  12. CAutoLock cObjectLock(m_pLock);
  13. m_bReadOnly = bReadOnly;
  14. //  If we modify data then don't accept the allocator if it's
  15. //  the same as the output pin's allocator
  16. //  If our output is not connected just accept the allocator
  17. //  We're never going to use this allocator because when our
  18. //  output pin is connected we'll reconnect this pin
  19. if (!m_pTIPFilter->OutputPin()->IsConnected()) {
  20. return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly);
  21. }
  22. //  If the allocator is read-only and we're modifying data
  23. //  and the allocator is the same as the output pin's
  24. //  then reject
  25. if (bReadOnly && m_pTIPFilter->m_bModifiesData) {
  26. IMemAllocator *pOutputAllocator =
  27. m_pTIPFilter->OutputPin()->PeekAllocator();
  28. //  Make sure we have an output allocator
  29. if (pOutputAllocator == NULL) {
  30. hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->
  31. GetAllocator(&pOutputAllocator);
  32. if(FAILED(hr)) {
  33. hr = CreateMemoryAllocator(&pOutputAllocator);
  34. }
  35. if (SUCCEEDED(hr)) {
  36. m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator);
  37. pOutputAllocator->Release();
  38. }
  39. }
  40. if (pAllocator == pOutputAllocator) {
  41. hr = E_FAIL;
  42. } else if(SUCCEEDED(hr)) {
  43. //  Must copy so set the allocator properties on the output
  44. ALLOCATOR_PROPERTIES Props, Actual;
  45. hr = pAllocator->GetProperties(&Props);
  46. if (SUCCEEDED(hr)) {
  47. hr = pOutputAllocator->SetProperties(&Props, &Actual);
  48. }
  49. if (SUCCEEDED(hr)) {
  50. if (  (Props.cBuffers > Actual.cBuffers)
  51. || (Props.cbBuffer > Actual.cbBuffer)
  52. || (Props.cbAlign  > Actual.cbAlign)
  53. ) {
  54. hr =  E_FAIL;
  55. }
  56. }
  57. //  Set the allocator on the output pin
  58. if (SUCCEEDED(hr)) {
  59. hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
  60. ->NotifyAllocator( pOutputAllocator, FALSE );
  61. }
  62. }
  63. } else {
  64. hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
  65. ->NotifyAllocator( pAllocator, bReadOnly );
  66. if (SUCCEEDED(hr)) {
  67. m_pTIPFilter->OutputPin()->SetAllocator( pAllocator );
  68. }
  69. }
  70. if (SUCCEEDED(hr)) {
  71. // It's possible that the old and the new are the same thing.
  72. // AddRef before release ensures that we don't unload it.
  73. pAllocator->AddRef();
  74. if( m_pAllocator != NULL )
  75. m_pAllocator->Release();
  76. m_pAllocator = pAllocator;    // We have an allocator for the input pin
  77. }
  78. return hr;
  79. } // NotifyAllocator

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

这个变量在CTransInPlaceFilter构造函数调用时被默认初始化为true。如果我们确定不会在Filter中修改Sample数据,

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

10、 CVideoTransformFilter

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

Filter组件开发中的SDK基类分析

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

CVieoTransformFilter类的使用基本上与CTransformFilter相同。

11、 CBaseRenderer

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

这两个类的结构如下:

Filter组件开发中的SDK基类分析

从图中可以看出,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类的基类,结构如下:

Filter组件开发中的SDK基类分析

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