DirectShow 学习(六): CTransfromFilter及相关联Pin类的源代码解析
1. CTransformInputPin类
派生自CBaseInputPin。
a) 成员变量
CTransformFilter *m_pTransformFilter;
b) IPin的接口函数:
STDMETHODIMP QueryId(LPWSTR * Id){ return AMGetWideString(L"In", Id); }
// provide EndOfStream that passes straight downstream
STDMETHODIMP EndOfStream(void);
{
CAutoLock lck(&m_pTransformFilter->m_csReceive);
HRESULT hr = CheckStreaming();
if (S_OK == hr) {
hr = m_pTransformFilter->EndOfStream();}
return hr;
}
// passes it to CTransformFilter::BeginFlush
STDMETHODIMP BeginFlush(void);
{
CAutoLock lck(&m_pTransformFilter->m_csFilter);
if (!IsConnected() ||
!m_pTransformFilter->m_pOutput->IsConnected()) {
return VFW_E_NOT_CONNECTED;}
HRESULT hr = CBaseInputPin::BeginFlush();
return m_pTransformFilter->BeginFlush();
}
// passes it to CTransformFilter::EndFlush
STDMETHODIMP EndFlush(void);
{
CAutoLock lck(&m_pTransformFilter->m_csFilter);
if (!IsConnected() ||
!m_pTransformFilter->m_pOutput->IsConnected()) {
return VFW_E_NOT_CONNECTED;}
HRESULT hr = m_pTransformFilter->EndFlush();
return CBaseInputPin::EndFlush();
}
STDMETHODIMP NewSegment(
REFERENCE_TIME tStart,
REFERENCE_TIME tStop,
double dRate);
{
// Save the values in the pin
CBasePin::NewSegment(tStart, tStop, dRate);
return m_pTransformFilter->NewSegment(tStart, tStop, dRate);
}
c) IMemInputPin的接口函数
// here's the next block of data from the stream.
// AddRef it yourself if you need to hold it beyond the end
// of this call.
STDMETHODIMP Receive(IMediaSample * pSample);
{
CAutoLock lck(&m_pTransformFilter->m_csReceive);
// check all is well with the base class
hr = CBaseInputPin::Receive(pSample);
if (S_OK == hr) { hr = m_pTransformFilter->Receive(pSample);}
return hr;
}
d) 从CBasePin和CBaseInputPin继承的函数:
HRESULT CheckConnect(IPin *pPin);
{
HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_INPUT,pPin);
return CBaseInputPin::CheckConnect(pPin);
}
HRESULT
BreakConnect();
{
m_pTransformFilter->
BreakConnect(PINDIR_INPUT);
return CBaseInputPin::
BreakConnect();
}
HRESULT CompleteConnect(IPin *pReceivePin);
{
HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
return CBaseInputPin::CompleteConnect(pReceivePin);
}
HRESULT CheckMediaType(const CMediaType* mtIn);
{
HRESULT hr = m_pTransformFilter->CheckInputType(pmt);
// if the output pin is still connected, then we have
// to check the transform not just the input format
if ((m_pTransformFilter->m_pOutput != NULL) &&
(m_pTransformFilter->m_pOutput->IsConnected())) {
return m_pTransformFilter->CheckTransform(
pmt,
&m_pTransformFilter->m_pOutput->CurrentMediaType());
} else {return hr;}
}
// set the connection media type
HRESULT SetMediaType(const CMediaType* mt);
{
// Set the base class media type (should always succeed)
HRESULT hr = CBasePin::SetMediaType(mtIn);
// check the transform can be done (should always succeed)
ASSERT(SUCCEEDED(m_pTransformFilter->CheckInputType(mtIn)));
return m_pTransformFilter->SetMediaType(PINDIR_INPUT,mtIn);
}
// Check if it's OK to process samples
virtual HRESULT CheckStreaming();
{
// 类似于CBaseInputPin的CheckStreaming函数,只是增加了先检查
// Transform Filter的Output Pin是否连接
}
2. CTransformOutputPin类
派生自CBaseOutputPin。
a) 成员变量:
CTransformFilter *m_pTransformFilter;
// implement IMediaPosition by passing upstream
IUnknown * m_pPosition;
b) NonDelegatingQueryInterface函数
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
// we should have an input pin by now
if (m_pPosition == NULL) {
HRESULT hr = CreatePosPassThru(
GetOwner(),FALSE, (IPin *)m_pTransformFilter->m_pInput, &m_pPosition);
return m_pPosition->QueryInterface(riid, ppv);
} else {
return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);}
}
STDAPI CreatePosPassThru(LPUNKNOWN pAgg, BOOL
bRenderer, IPin *pPin,
IUnknown **ppPassThru )
{
HRESULT hr = CoCreateInstance(CLSID_SeekingPassThru,
pAgg, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pUnkSeek );
ISeekingPassThru *pPassThru;
hr = pUnkSeek->QueryInterface(IID_ISeekingPassThru, (void**)&pPassThru);
hr = pPassThru->Init(
bRenderer, pPin);
pPassThru->Release();
*ppPassThru = pUnkSeek;
}
c) IPin的接口函数
STDMETHODIMP QueryId(LPWSTR * Id){ return AMGetWideString(L"Out", Id);}
d) IQualityControl的接口函数
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
{
// First see if we want to handle this ourselves
HRESULT hr = m_pTransformFilter->AlterQuality(q);
if (hr!=S_FALSE) { return hr; // either S_OK or a failure }
return m_pTransformFilter->m_pInput->PassNotify(q);
}
e) 从CBasePin和CBaseInputPin继承的函数:
HRESULT CheckConnect(IPin *pPin);
{
if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {
return E_UNEXPECTED;
}
HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_OUTPUT,pPin);
return CBaseOutputPin::CheckConnect(pPin);
}
HRESULT
BreakConnect();
{
m_pTransformFilter->
BreakConnect(PINDIR_OUTPUT);
return CBaseOutputPin::
BreakConnect();
}
HRESULT CompleteConnect(IPin *pReceivePin);
{
HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
return CBaseOutputPin::CompleteConnect(pReceivePin);
}
// check that we can support this output type
HRESULT CheckMediaType(const CMediaType* mtOut);
{
if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE))
{ return E_INVALIDARG;}
return m_pTransformFilter->CheckTransform(
&m_pTransformFilter->m_pInput->CurrentMediaType(),pmtOut);
}
// set the connection media type, called after we have agreed a media type to
// actually set it in which case we run the CheckTransform function to get the
// output format type again
HRESULT SetMediaType(const CMediaType *pmt);
{
hr = CBasePin::SetMediaType(pmtOut);
return m_pTransformFilter->SetMediaType(PINDIR_OUTPUT,pmtOut);
}
// called from CBaseOutputPin during connection to ask for
// the count and size of buffers we need.
HRESULT DecideBufferSize(
IMemAllocator * pAlloc,
ALLOCATOR_PROPERTIES *pProp);
{
return m_pTransformFilter->DecideBufferSize(pAllocator, pProp);
}
// returns the preferred formats for a pin
HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
{
if (m_pTransformFilter->m_pInput->IsConnected()) {
return m_pTransformFilter->GetMediaType(iPosition,pMediaType);
} else {
return VFW_S_NO_MORE_ITEMS;
}
}
3. CTransformFilter类
派生自CBaseFilter
a) 成员变量
BOOL m_bEOSDelivered; // have we sent EndOfStream
BOOL m_bSampleSkipped; // Did we just skip a frame
BOOL m_bQualityChanged; // Have we degraded?
// critical section protecting filter state.
CCritSec m_csFilter;
// critical section stopping state changes (ie Stop) while we're
// processing a sample. This critical section is held when processing
// events that occur on the receive thread - Receive() and EndOfStream().
// If you want to hold both m_csReceive and m_csFilter then grab
// m_csFilter FIRST - like CTransformFilter::Stop() does.
CCritSec m_csReceive;
// these hold our input and output pins
CTransformInputPin *m_pInput;
CTransformOutputPin *m_pOutput;
b) 新增的virtual 函数:
// Transform,必须override
virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
// check if you can support mtIn,必须override
virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
// check if you can support the transform from this input to this output,必须override
virtual HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) PURE;
// call the SetProperties function with appropriate arguments,必须override
virtual HRESULT DecideBufferSize(
IMemAllocator * pAllocator,
ALLOCATOR_PROPERTIES *pprop) PURE;
// override to suggest OUTPUT pin media types,必须override
virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
// you can also override these if you want to know about streaming
virtual HRESULT StartStreaming();{return NOERROR; }
virtual HRESULT StopStreaming();{return NOERROR;}
// override if you can do anything constructive with quality notifications
virtual HRESULT AlterQuality(Quality q);
{
// Return S_FALSE to mean "pass the note on upstream"
// Return NOERROR (Same as S_OK)
// to mean "I've done something about it, don't pass it on"
return S_FALSE;
}
// override this to know when the media type is actually set
virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
{ return NOERROR;}
// chance to grab extra interfaces on connection
virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);{ return NOERROR;}
virtual HRESULT
BreakConnect(PIN_DIRECTION dir);{ return NOERROR;}
virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
{ return NOERROR;}
// chance to customize the transform process
virtual HRESULT Receive(IMediaSample *pSample);
{
/* Check for other streams and pass them on */
AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
if (pProps->dwStreamId != AM_STREAM_MEDIA) {
return m_pOutput->m_pInputPin->Receive(pSample);
}
IMediaSample * pOutSample;
// Set up the output sample
hr = InitializeOutputSample(pSample, &pOutSample);
// have the derived class transform the data
hr = Transform(pSample, pOutSample);
// the Transform() function can return S_FALSE to indicate that the
// sample should not be delivered; we only deliver the sample if it's
// really S_OK (same as NOERROR, of course.)
if (hr == NOERROR) {
hr = m_pOutput->m_pInputPin->Receive(pOutSample);
m_bSampleSkipped = FALSE; // last thing no longer dropped
} else {
// S_FALSE returned from Transform is a PRIVATE agreement
// We should return NOERROR from Receive() in this cause because
// returning S_FALSE from Receive() means that this is the end of
// the stream and no more data should be sent.
if (S_FALSE == hr) {
// Release the sample before calling notify to avoid
// deadlocks if the sample holds a lock on the system
// such as DirectDraw buffers do
pOutSample->Release();
m_bSampleSkipped = TRUE;
if (!m_bQualityChanged) {
NotifyEvent(EC_QUALITY_CHANGE,0,0);
m_bQualityChanged = TRUE;
}
return NOERROR;
}
}
pOutSample->Release();
}
// if you override Receive, you may need to override these three too
virtual HRESULT EndOfStream(void);
{
hr = m_pOutput->DeliverEndOfStream();
}
virtual HRESULT BeginFlush(void);
{
hr = m_pOutput->DeliverBeginFlush();
}
virtual HRESULT EndFlush(void);
{
return m_pOutput->DeliverEndFlush();
}
virtual HRESULT NewSegment(
REFERENCE_TIME tStart,
REFERENCE_TIME tStop,
double dRate);
{
return m_pOutput->DeliverNewSegment(tStart, tStop, dRate);
}
c) 继承CBaseFilter的函数:
virtual int GetPinCount();{return 2; }
virtual CBasePin * GetPin(int n);
{
// Create an input pin if necessary
if (m_pInput == NULL) {
m_pInput = new CTransformInputPin(NAME("Transform input pin"),
this, // Owner filter
&hr, // Result code
L"XForm In"); // Pin name
m_pOutput = (CTransformOutputPin *)
new CTransformOutputPin(NAME("Transform output pin"),
this, // Owner filter
&hr, // Result code
L"XForm Out"); // Pin name
}
// Return the appropriate pin
if (n == 0) { return m_pInput;}
else if (n == 1) { return m_pOutput;}
else { return NULL;}
}
STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
{
if (0==lstrcmpW(Id,L"In")) { *ppPin = GetPin(0);}
else if (0==lstrcmpW(Id,L"Out")) { *ppPin = GetPin(1); }
if (*ppPin) { (*ppPin)->AddRef();}
}
STDMETHODIMP Stop();
{
CAutoLock lck1(&m_csFilter);
if (m_State == State_Stopped) { return NOERROR; }
if (m_pInput == NULL || m_pInput->IsConnected() == FALSE ||
m_pOutput->IsConnected() == FALSE) {
m_State = State_Stopped;
m_bEOSDelivered = FALSE;
return NOERROR;
}
// decommit the input pin before locking or we can deadlock
m_pInput->Inactive();
// synchronize with Receive calls
CAutoLock lck2(&m_csReceive);
m_pOutput->Inactive();
// allow a class derived from CTransformFilter
// to know about starting and stopping streaming
HRESULT hr = StopStreaming();
if (SUCCEEDED(hr)) {
// complete the state transition
m_State = State_Stopped;
m_bEOSDelivered = FALSE;
}
}
STDMETHODIMP Pause();
{
CAutoLock lck(&m_csFilter);
if (m_State == State_Paused) {}
else if (m_pInput == NULL || m_pInput->IsConnected() == FALSE) {
if (m_pOutput && m_bEOSDelivered == FALSE) {
m_pOutput->DeliverEndOfStream();
m_bEOSDelivered = TRUE;
}
m_State = State_Paused; }
else if (m_pOutput->IsConnected() == FALSE) {
m_State = State_Paused;}
else {
if (m_State == State_Stopped) {
CAutoLock lck2(&m_csReceive);
hr = StartStreaming();}
if (SUCCEEDED(hr)) { hr = CBaseFilter::Pause();}
}
m_bSampleSkipped = FALSE;
m_bQualityChanged = FALSE;
}
d) 其他函数
// Standard setup for output sample
HRESULT InitializeOutputSample(IMediaSample *pSample, IMediaSample **ppOutSample);
{
IMediaSample *pOutSample;
AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;
if (!(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) {
dwFlags |= AM_GBF_NOTASYNCPOINT; }
HRESULT hr = m_pOutput->m_pAllocator->GetBuffer(
&pOutSample
, pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ? &pProps->tStart : NULL
, pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ? &pProps->tStop : NULL
, dwFlags
);
*ppOutSample = pOutSample;
// 如果支持IMediaSample2接口,则调用该接口来设置属性,否则调用IMediaSample来设置
}