几篇关于如何写filter的转帖 之 ----开发source filter的source sourcestream两个基类介绍
第一个是source,第二个是sourcestream
3.3几种常用Filter的基类
3.3.1CSource
class CSource : public CBaseFilter {
public:
CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
#ifdef UNICODE
CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr);
CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid);
#endif
~CSource();
int GetPinCount(void);
CBasePin *GetPin(int n);
CCritSec* pStateLock(void) { return &m_cStateLock; }
HRESULT AddPin(CSourceStream *);
HRESULT RemovePin(CSourceStream *);
STDMETHODIMP FindPin(LPCWSTR Id,IPin ** ppPin );
int FindPinNumber(IPin *iPin);
protected:
int m_iPins;
CSourceStream **m_paStreams; // the pins on this filter.
CCritSec m_cStateLock; // Lock this to serialize
};
CSource类是开发源Filter的基类,一个从从CSource类派生的Filter一般都支持一个或者几个从CSourceSteam类派生的输出pin。每一个输出pin都创建了一个工作线程用来将数据传递给下游的Filter。
为了生产一个输出pin,请按照如下的步骤
1 从CSoureceStream类派生一个类。
2重载CSourceStream::GetMediaType和CSourceStream::CheckMediaType方法。
3重载CBaseOutputPin::DecideBufferSize,这个方法将返回pin的buffer要求
4重载CSourceStream::FillBuffer,这个方法用来产生数据流,将数据填充到buffer中
为了派生一个源Filter,可以按照下面的步骤来做
1从CSourec类派生一个类。
2在构造函数中创建一个或者多个的输出pin。注意,这个pin要从CSourceStream派生哦。这些pin会在他们的构造函数中自动将自己添加到Filter中。并且在他们的析构函数种从Filter中删除Pin。
为了在多线程中同步Filter的状态,调用CSource::pStateLock方法,这个方法返回一个指向临界区的指针,通过CAutoLock来生成一个锁,在pin中,你可以通过CBasePin::m_pFilter来指针来操纵临界区保护你的Filter,从而达到同步。例如
CAutoLock lock(m_pFilter->pStateLock());
注意:CSource用来生成推模式的源Filter,如果要读文件,则应该使用拉模式。
下面我们分析一下CSourec的数据成员
int m_iPins;
CSourceStream **m_paStreams;
CCritSec m_cStateLock;
总共三个数据成员,一个表示pin的数量,一个pin的数组,用来存放Filter支持的所有的pin,另外一个是临界区对象。
下面分析方法,也比较简单。
1CSource::GetPinCount
这个函数用来返回Filter支持的pin的数目。
2CSource::GetPin
CBasePin *GetPin( int n);
根据指定的序号返回pin的指针。0,1,2,就是从Filter的那个pin数组中返回就是了,根据序号。
3CCritSec* pStateLock(void);
返回临界区对象
4CSource::AddPin
HRESULT AddPin( CSourceStream *pStream);
构造函数通常调用这个函数将一个输出pin添加到Filter中。
5CSource::RemovePin
HRESULT RemovePin( CSourceStream *pStream);
析构函数通常通过这个函数将一个输出pin从Filter中删除
6CSource::FindPinNumber
int FindPinNumber( IPin *iPin);
这个函数根据指定的pin的指针,返回他的序号,如果返回-1表示没有这个pin。
7CSource::FindPin
HRESULT FindPin( LPCWSTR Id, IPin **ppPin);
根据指定的ID返回pin。
注意,Filter的第一个pin ID为1开始,然后是23456789等。
3.3.2CSourceStream
class CSourceStream : public CAMThread, public CBaseOutputPin {
public:
CSourceStream(TCHAR *pObjectName,
HRESULT *phr,
CSource *pms,
LPCWSTR pName);
#ifdef UNICODE
CSourceStream(CHAR *pObjectName,
HRESULT *phr,
CSource *pms,
LPCWSTR pName);
#endif
virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too.
protected:
CSource *m_pFilter; // The parent of this stream
virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE;
virtual HRESULT OnThreadCreate(void) {return NOERROR;};
virtual HRESULT OnThreadDestroy(void) {return NOERROR;};
virtual HRESULT OnThreadStartPlay(void) {return NOERROR;};
HRESULT Active(void); // Starts up the worker thread
HRESULT Inactive(void); // Exits the worker thread.
public:
// thread commands
enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT};
HRESULT Init(void) { return CallWorker(CMD_INIT); }
HRESULT Exit(void) { return CallWorker(CMD_EXIT); }
HRESULT Run(void) { return CallWorker(CMD_RUN); }
HRESULT Pause(void) { return CallWorker(CMD_PAUSE); }
HRESULT Stop(void) { return CallWorker(CMD_STOP); }
protected:
Command GetRequest(void) { return (Command) CAMThread::GetRequest(); }
BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); }
// override these if you want to add thread commands
virtual DWORD ThreadProc(void); // the thread function
virtual HRESULT DoBufferProcessingLoop(void);
virtual HRESULT CheckMediaType(const CMediaType *pMediaType);
virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
virtual HRESULT GetMediaType(CMediaType *pMediaType) {return E_UNEXPECTED;}
STDMETHODIMP QueryId(
LPWSTR * Id
);
};
CSourceStream类用来生成一个输出pin,当然为了CSource配合。
关于如何使用这个类,可以参看CSource,这个类从CAMThread类继承而来,这个类提供了产生了数据流的工作线程,CSourceStream类通过下面的方法来给线程发送请求。
CSourceStream::Exit,CSourceStream::Init,CSourceStream::Pause,CSourceStream::Run,
CSourceStream::Stop
发给线程的第一个请求一定是Init,Exit请求用来结束线程,实际上,我们没有必要亲自调用这些函数,因为在CSourceStream::Active and CSourceStream::Inactive都调用了所需要的方法。
这个类也提供几个handler方法
CSourceStream::OnThreadCreate
CSourceStream::OnThreadDestroy
CSourceStream::OnThreadStartPlay
首先分析一下数据成员
CSource *m_pFilter;
靠,就一个数据成员,用来指向这个pin连接的Filter。
看看成员函数吧,
1CSourceStream::OnThreadCreate
virtual HRESULT OnThreadCreate(void);
线程处理CSourceStream::ThreadProc在第一次接收到Init请求的时候,就会调用这个方法,在基类的实现中,这个函数没有作任何的事情,派生类可以在这个函数中做一些初始化线程的工作。
2CSourceStream::OnThreadDestroy
当线程退出的时候,会调用这个函数,基类的实现没有作任何的事情,你可以在派生类中做清理现场的工作。
3CSourceStream::OnThreadStartPlay
在CSourceStream::DoBufferProcessingLoop开始的时候,会调用到这个函数
4CSourceStream::Active
5CSourceStream::Inactive
6CSourceStream::GetRequest
Command GetRequest(void);
这个函数可以等待下一个线程请求。这个方法重载了CAMThread::GetRequest
7CSourceStream::CheckRequest
非阻塞情况下,这个函数用来查询是否有个线程请求
8CSourceStream::ThreadProc
这个函数是工作线程的函数体,重载了CAMThread::ThreadProc方法。
virtual DWORD ThreadProc(void);
这个函数会无限的等待线程的请求,通过调用CAMThread::GetRequest方法,如果它接收到CSourceStream::Run or CSourceStream::Pause请求,它就调用
CSourceStream::DoBufferProcessingLoop方法,DoBufferProcessingLoop会一直往外推数据,直到它接收到一个CSourceStream::Stop请求,线程处理器当接收到CSourceStream::Exit请求,就会退出了。
9CSourceStream::DoBufferProcessingLoop
virtual HRESULT DoBufferProcessingLoop(void);
这个方法其实是线程的实现函数,在这个循环中处理数据,并且将数据传递给下下游的Filter,每次,这个方法都会申请一个空的内存sample,然后将这个sample传递给CSourceStream::FillBuffer方法,FillBuffer方法在派生类中一定要实现哦,这个函数是用来产生数据,并将数据拷贝到sample。
当发生下面的情形时,循环停止,退出
1 当IMemInputPin::Receive方法拒绝samples。
2FillBuffer返回False,表示结束发送数据了
3线程接收到一个CSourceStream::Stop请求。
10CSourceStream::CheckMediaType
virtual HRESULT CheckMediaType( const CMediaType *pMediaType);
确认是否支持指定的媒体类型。
11CSourceStream::GetMediaType
virtual HRESULT GetMediaType( int iPosition, CMediaType *pMediaType);
virtual HRESULT GetMediaType( CMediaType *pMediaType);
获取指定位置的媒体类型。
12CSourceStream::Init
HRESULT Init(void);
这个函数用来启动一个线程,CSourceStream::Active方法会调用这个函数的。
13CSourceStream::Exit
The CSourceStream::Inactive method calls this method.
14CSourceStream::Run
15CSourceStream::Pause
CSourceStream::Active会调用这个方法的。当CSourceStream::ThreadProc接收这个请求,它会调用CSourceStream::DoBufferProcessingLoop方法。
16CSourceStream::Stop
The CSourceStream::Inactive method calls this method.
17CSourceStream::FillBuffer
virtual HRESULT FillBuffer( IMediaSample *pSample) PURE;
最重要的一个函数。
派生类中一定要实现这个函数,媒体samples没有给这个方法提供时间戳,派生类应该调用IMediaSample::SetTime方法来设置时间戳。