DirectShow 学习(七): CTransInPlaceFilter及相关联Pin类的源代码解析(转载)
2007-10-14 10:10
351 查看
DirectShow 学习(七): CTransInPlaceFilter及相关联Pin类的源代码解析
1. CTransInPlaceInputPin类[transip.h/transip.cpp]
派生自CTransformInputPin。
a) 成员变量:
CTransInPlaceFilter * const m_pTIPFilter; // our filter
BOOL m_bReadOnly; // incoming stream is read only
m_bReadOnly初始化为FALSE。
b) IMemInputPin接口函数
// Return our upstream allocator
// If the downstream filter has one then offer that (even if our own output pin is not using it yet.
// If the upstream filter chooses it then we will tell our output pin to ReceiveAllocator).
// Else if our output pin is using an allocator then offer that.
// ( This could mean offering the upstream filter his own allocator, it could mean offerring our own
// ) or it could mean offering the one from downstream
// Else fail to offer any allocator at all.
STDMETHODIMP GetAllocator(IMemAllocator ** ppAllocator);
{
CAutoLock cObjectLock(m_pLock);
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 {hr = CTransformInputPin::GetAllocator( ppAllocator );}
}
// get told which allocator the upstream output pin is actually going to use.
STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator, BOOL bReadOnly);
{
CAutoLock cObjectLock(m_pLock);
m_bReadOnly = bReadOnly;
// If we modify data then don't accept the allocator if it's the same as the output pin's allocator
// If our output is not connected just accept the allocator
// We're never going to use this allocator because when our
// output pin is connected we'll reconnect this pin
if (!m_pTIPFilter->OutputPin()->IsConnected()) {
return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly);}
// If the allocator is read-only and we're modifying data
// and the allocator is the same as the output pin's then reject
if (bReadOnly && m_pTIPFilter->m_bModifiesData) {
IMemAllocator *pOutputAllocator =
m_pTIPFilter->OutputPin()->PeekAllocator();
// Make sure we have an output allocator
if (pOutputAllocator == NULL) {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->
GetAllocator(&pOutputAllocator);
if(FAILED(hr)) {hr = CreateMemoryAllocator(&pOutputAllocator);}
if (SUCCEEDED(hr)) {
m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator);
pOutputAllocator->Release(); }
}
if (pAllocator == pOutputAllocator) {hr = E_FAIL;}
else if(SUCCEEDED(hr)) {
// Must copy so set the allocator properties on the output
ALLOCATOR_PROPERTIES Props, Actual;
hr = pAllocator->GetProperties(&Props);
if (SUCCEEDED(hr)) {
hr = pOutputAllocator->SetProperties(&Props, &Actual);}
if (SUCCEEDED(hr)) {
if ( (Props.cBuffers > Actual.cBuffers)
|| (Props.cbBuffer > Actual.cbBuffer)
|| (Props.cbAlign > Actual.cbAlign)
) {hr = E_FAIL;}
}
// Set the allocator on the output pin
if (SUCCEEDED(hr)) {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
->NotifyAllocator( pOutputAllocator, FALSE ); }
}
} else {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
->NotifyAllocator( pAllocator, bReadOnly );
if (SUCCEEDED(hr)) {
m_pTIPFilter->OutputPin()->SetAllocator( pAllocator );}
}
if (SUCCEEDED(hr)) {
// It's possible that the old and the new are the same thing.
// AddRef before release ensures that we don't unload it.
pAllocator->AddRef();
if( m_pAllocator != NULL ) m_pAllocator->Release();
m_pAllocator = pAllocator; // We have an allocator for the input pin
}
}
// Pass this on downstream if it ever gets called.
STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps);
{
if( m_pTIPFilter->m_pOutput->IsConnected() )
return m_pTIPFilter->OutputPin()
->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps );
else return E_NOTIMPL;
}
c) IPin接口函数以及CBasePin、CBaseInputPin继承函数
// Provide an enumerator for media types by getting one from downstream
STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );
{
// Can only pass through if connected
if( !m_pTIPFilter->m_pOutput->IsConnected() )
return VFW_E_NOT_CONNECTED;
return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes( ppEnum );
}
// Say whether media type is acceptable. - agree to anything if not connected,
// otherwise pass through to the downstream filter.
// This assumes that the filter does not change the media type.
HRESULT CheckMediaType(const CMediaType* pmt);
{
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
if( m_pTIPFilter->m_pOutput->IsConnected() )
return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt );
else return S_OK;
}
HRESULT CompleteConnect(IPin *pReceivePin);
{
HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin);
return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
}
d) 其他函数
// Allow the filter to see what allocator we have N.B. This does NOT AddRef
IMemAllocator * PeekAllocator() const{ return m_pAllocator; }
inline const BOOL ReadOnly() { return m_bReadOnly ; }
2. CTransInPlaceOutputPin类[transip.h/transip.cpp]
a) 成员变量
CTransInPlaceFilter * const m_pTIPFilter;
b) IPin接口函数,CBaseOutputPin函数
// Provide a media type enumerator. Get it from upstream.
STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );
{
if( ! m_pTIPFilter->m_pInput->IsConnected() )
return VFW_E_NOT_CONNECTED;
return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum );
}
// Say whether media type is acceptable. - agree to anything if not connected,
// otherwise pass through to the upstream filter.
HRESULT CheckMediaType(const CMediaType* pmt);
{
if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) {
if (*pmt == m_mt) {return S_OK;}
else {return VFW_E_TYPE_NOT_ACCEPTED;}
}
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
if( m_pTIPFilter->m_pInput->IsConnected() )
return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt );
else return S_OK;
}
HRESULT CompleteConnect(IPin *pReceivePin);
{
HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
}
c) 其他函数
// This just saves the allocator being used on the output pin
// Also called by input pin's GetAllocator()
void SetAllocator(IMemAllocator * pAllocator);
{
pAllocator->AddRef();
if (m_pAllocator) {m_pAllocator->Release();}
m_pAllocator = pAllocator;
}
IMemInputPin * ConnectedIMemInputPin(){ return m_pInputPin; }
IMemAllocator * PeekAllocator() const{ return m_pAllocator; }
3. CTransInPlaceFilter类[transip.h/transip.cpp]
a) 成员变量
bool m_bModifiesData; // Does this filter change the data?
m_bModifiesData被Constructor中bModifiesData初始化。
b) 新增加的virtual函数
virtual HRESULT Transform(IMediaSample *pSample) PURE;
c) 继承的IBaseFilter、CBaseFilter和CTransformFilter函数
// 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->Deliver(pSample);}
if (UsingDifferentAllocators()) {
pSample = Copy(pSample);}
// have the derived class transform the data
hr = Transform(pSample);
hr = m_pOutput->Deliver(pSample);
if (UsingDifferentAllocators()) {pSample->Release();}
}
HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin);
{
if (dir == PINDIR_OUTPUT) {
if( m_pInput->IsConnected() ) {
return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() );}
return NOERROR;
}
if( m_pOutput->IsConnected() ) {
if ( m_pInput->CurrentMediaType()
!= m_pOutput->CurrentMediaType()
) {
return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() );
}
}
}
// 必须override
HRESULT GetMediaType填充 ALLOCATOR_PROPERTIES,并调用pAlloc->SetProperties(pProperties, &Actual);
}
// [b]必须override
HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)
virtual CBasePin *GetPin(int n);
{
// Create an input pin if not already done
if (m_pInput == NULL) {
m_pInput = new CTransInPlaceInputPin( NAME("TransInPlace input pin")
, this // Owner filter
, &hr // Result code
, L"Input" // Pin name
);
if (m_pInput!=NULL && m_pOutput == NULL) {
m_pOutput = new CTransInPlaceOutputPin( NAME("TransInPlace output pin")
, this // Owner filter
, &hr // Result code
, L"Output" // Pin name
);
if (n == 0) {return m_pInput;}
else if (n==1) {return m_pOutput;}
else {return NULL;}
}[/b](int iPosition, CMediaType *pMediaType)
// This is called when we actually have to provide out own allocator.
HRESULT DecideBufferSize(IMemAllocator*, ALLOCATOR_PROPERTIES *);
{
// If we are connected upstream, get his views
if (m_pInput->IsConnected()) {
hr = InputPin()->PeekAllocator()->GetProperties(&Request); }
//
d) 其他函数
IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource);
{复制两个Sample}
CTransInPlaceInputPin *InputPin() const{return (CTransInPlaceInputPin *)m_pInput;}
CTransInPlaceOutputPin *OutputPin() const
{return (CTransInPlaceOutputPin *)m_pOutput;}
// Helper to see if the input and output types match
BOOL TypesMatch()
{return InputPin()->CurrentMediaType() ==OutputPin()->CurrentMediaType();}
// Are the input and output allocators different?
BOOL UsingDifferentAllocators() const
{return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator();}
[align=left]附上CTransInPlaceFilter的中的设计思路注释,这几个类应该是Base Classes中注释最多,最容易读的类了。
// How allocators are decided.[/align]
[align=left]// An in-place transform tries to do its work in someone else's buffers. It tries to persuade[/align]
[align=left]// the filters on either side to use the same allocator (and for that matter the same media type).[/align]
[align=left]// In desperation, if the downstream filter refuses to supply an allocator and the upstream filter[/align]
[align=left]// offers only a read-only one then it will provide an allocator. if the upstream filter insists[/align]
[align=left]// on a read-only allocator then the transform filter will (reluctantly) copy the data before[/align]
[align=left]// transforming it[/align]
[align=left]// In order to pass an allocator through it needs to remember the one it got from the first[/align]
[align=left]// connection to pass it on to the second one.[/align]
[align=left]// It is good if we can avoid insisting on a particular order of connection (There is a precedent[/align]
[align=left]// for insisting on the input being connected first. Insisting on the output being connected[/align]
[align=left]// first is not allowed. That would break RenderFile.)[/align]
[align=left]// The base pin classes (CBaseOutputPin and CBaseInputPin) both have a m_pAllocator member which[/align]
[align=left]// is used in places like CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive.[/align]
[align=left]// To avoid lots of extra overriding, we should keep these happy by using these pointers.[/align]
[align=left]// When each pin is connected, it will set the corresponding m_pAllocator and will have a single[/align]
[align=left]// ref-count on that allocator Refcounts are acquired by GetAllocator calls which return AddReffed[/align]
[align=left]// allocators and are released in one of:[/align]
[align=left]// CBaseInputPin::Disconnect[/align]
[align=left]// CBaseOutputPin::BreakConect[/align]
[align=left]// In each case m_pAllocator is set to NULL after the release, so this is the last chance to ever[/align]
[align=left]// release it. If there should ever be multiple refcounts associated with the same pointer, this[/align]
[align=left]// had better be cleared up before that happens. To avoid such problems, we'll stick with one[/align]
[align=left]// per pointer.[/align]
[align=left] [/align]
[align=left]// RECONNECTING and STATE CHANGES[/align]
[align=left]// Each pin could be disconnected, connected with a read-only allocator, connected with an upstream[/align]
[align=left]// read/write allocator, connected with an allocator from downstream or connected with its own[/align]
[align=left]// allocator. Five states for each pin gives a data space of 25 states.[/align]
[align=left] [/align]
[align=left]// Notation:[/align]
[align=left]// R/W == read/write[/align]
[align=left]// R-O == read-only[/align]
[align=left]// <input pin state> <output pin state> <comments>[/align]
[align=left]//[/align]
[align=left]// 00 means an unconnected pin.[/align]
[align=left]// <- means using a R/W allocator from the upstream filter[/align]
[align=left]// <= means using a R-O allocator from an upstream filter[/align]
[align=left]// || means using our own (R/W) allocator.[/align]
[align=left]// -> means using a R/W allocator from a downstream filter[/align]
[align=left]// (a R-O allocator from downstream is nonsense, it can't ever work).[/align]
[align=left]//[/align]
[align=left]// That makes 25 possible states. Some states are nonsense (two different[/align]
[align=left]// allocators from the same place). These are just an artifact of the notation.[/align]
[align=left]// <= <- Nonsense.[/align]
[align=left]// <- <= Nonsense[/align]
[align=left]// Some states are illegal (the output pin never accepts a R-O allocator):[/align]
[align=left]// 00 <= !! Error !![/align]
[align=left]// <= <= !! Error !![/align]
[align=left]// || <= !! Error !![/align]
[align=left]// -> <= !! Error !![/align]
[align=left]// Three states appears to be inaccessible:[/align]
[align=left]// -> || Inaccessible[/align]
[align=left]// || -> Inaccessible[/align]
[align=left]// || <- Inaccessible[/align]
[align=left]// Some states only ever occur as intermediates with a pending reconnect which[/align]
[align=left]// is guaranteed to finish in another state.[/align]
[align=left]// -> 00 ?? unstable goes to || 00[/align]
[align=left]// 00 <- ?? unstable goes to 00 ||[/align]
[align=left]// -> <- ?? unstable goes to -> ->[/align]
[align=left]// <- || ?? unstable goes to <- <-[/align]
[align=left]// <- -> ?? unstable goes to <- <-[/align]
[align=left]// And that leaves 11 possible resting states:[/align]
[align=left]// 1 00 00 Nothing connected.[/align]
[align=left]// 2 <- 00 Input pin connected.[/align]
[align=left]// 3 <= 00 Input pin connected using R-O allocator.[/align]
[align=left]// 4 || 00 Needs several state changes to get here.[/align]
[align=left]// 5 00 || Output pin connected using our allocator[/align]
[align=left]// 6 00 -> Downstream only connected[/align]
[align=left]// 7 || || Undesirable but can be forced upon us.[/align]
[align=left]// 8 <= || Copy forced. <= -> is preferable[/align]
[align=left]// 9 <= -> OK - forced to copy.[/align]
[align=left]// 10 <- <- Transform in place (ideal)[/align]
[align=left]// 11 -> -> Transform in place (ideal)[/align]
[align=left]//[/align]
[align=left]// The object of the exercise is to ensure that we finish up in states 10 or 11 whenever possible.[/align]
[align=left]// State 10 is only possible if the upstream filter has a R/W allocator (the AVI splitter[/align]
[align=left]// notoriously doesn't) and state 11 is only possible if the downstream filter does offer an[/align]
[align=left]// allocator.[/align]
[align=left]// The transition table (entries marked * go via a reconnect)[/align]
[align=left]// There are 8 possible transitions:[/align]
[align=left]// A: Connect upstream to filter with R-O allocator that insists on using it.[/align]
[align=left]// B: Connect upstream to filter with R-O allocator but chooses not to use it.[/align]
[align=left]// C: Connect upstream to filter with R/W allocator and insists on using it.[/align]
[align=left]// D: Connect upstream to filter with R/W allocator but chooses not to use it.[/align]
[align=left]// E: Connect downstream to a filter that offers an allocator[/align]
[align=left]// F: Connect downstream to a filter that does not offer an allocator[/align]
[align=left]// G: disconnect upstream[/align]
[align=left]// H: Disconnect downstream[/align]
[align=left]//[/align]
[align=left]// A B C D E F G H[/align]
[align=left]// ---------------------------------------------------------[/align]
[align=left]// 00 00 1 | 3 3 2 2 6 5 . . |1 00 00[/align]
[align=left]// <- 00 2 | . . . . *10/11 10 1 . |2 <- 00[/align]
[align=left]// <= 00 3 | . . . . *9/11 *7/8 1 . |3 <= 00[/align]
[align=left]// || 00 4 | . . . . *8 *7 1 . |4 || 00[/align]
[align=left]// 00 || 5 | 8 7 *10 7 . . . 1 |5 00 ||[/align]
[align=left]// 00 -> 6 | 9 11 *10 11 . . . 1 |6 00 ->[/align]
[align=left]// || || 7 | . . . . . . 5 4 |7 || ||[/align]
[align=left]// <= || 8 | . . . . . . 5 3 |8 <= ||[/align]
[align=left]// <= -> 9 | . . . . . . 6 3 |9 <= ->[/align]
[align=left]// <- <- 10| . . . . . . *5/6 2 |10 <- <-[/align]
[align=left]// -> -> 11| . . . . . . 6 *2/3 |11 -> ->[/align]
[align=left]// ---------------------------------------------------------[/align]
[align=left]// A B C D E F G H[/align]
[align=left]//[/align]
[align=left]// All these states are accessible without requiring any filter to change its behaviour but not[/align]
[align=left]// all transitions are accessible, // for instance a transition from state 4 to anywhere other[/align]
[align=left]// than state 8 requires that the upstream filter first offer a R-O allocator and then changes[/align]
[align=left]// its mind and offer R/W. This is NOT allowable - it leads to things like the output pin [/align]
[align=left]// getting a R/W allocator from upstream and then the input pin being told it can only have a[/align]
[align=left]// R-O one.[/align]
[align=left]// Note that you CAN change (say) the upstream filter for a different one, but only as a disconnect // connect, not as a Reconnect. (Exercise for the reader is to see how you get into state 4).[/align]
[align=left]//[/align]
[align=left]// The reconnection stuff goes as follows (some of the cases shown here as "no reconnect" may[/align]
[align=left]// get one to finalise media type - an old story). If there is a reconnect where it says "no[/align]
[align=left]// reconnect" here then the reconnection must not change the allocator choice.[/align]
[align=left]// state 2: <- 00 transition E <- <- case C <- <- (no change)[/align]
[align=left]// case D -> <- and then to -> ->[/align]
[align=left]//[/align]
[align=left]// state 2: <- 00 transition F <- <- (no reconnect)[/align]
[align=left]//[/align]
[align=left]// state 3: <= 00 transition E <= -> case A <= -> (no change)[/align]
[align=left]// case B -> ->[/align]
[align=left]// transition F <= || case A <= || (no change)[/align]
[align=left]// case B || ||[/align]
[align=left]//[/align]
[align=left]// state 4: || 00 transition E || || case B -> || and then all cases to -> ->[/align]
[align=left]// F || || case B || || (no change)[/align]
[align=left]//[/align]
[align=left]// state 5: 00 || transition A <= || (no reconnect)[/align]
[align=left]// B || || (no reconnect)[/align]
[align=left]// C <- || all cases <- <-[/align]
[align=left]// D || || (unfortunate, but upstream's choice)[/align]
[align=left]//[/align]
[align=left]// state 6: 00 -> transition A <= -> (no reconnect)[/align]
[align=left]// B -> -> (no reconnect)[/align]
[align=left]// C <- -> all cases <- <-[/align]
[align=left]// D -> -> (no reconnect)[/align]
[align=left]//[/align]
[align=left]// state 10:<- <- transition G 00 <- case E 00 ->[/align]
[align=left]// case F 00 ||[/align]
[align=left]//[/align]
[align=left]// state 11:-> -> transition H -> 00 case A <= 00 (schizo)[/align]
[align=left]// case B <= 00[/align]
[align=left]// case C <- 00 (schizo)[/align]
[align=left]// case D <- 00[/align]
[align=left]//[/align]
[align=left]// The Rules:[/align]
[align=left]// To sort out media types:[/align]
[align=left]// The input is reconnected[/align]
[align=left]// if the input pin is connected and the output pin connects[/align]
[align=left]// The output is reconnected[/align]
[align=left]// If the output pin is connected[/align]
[align=left]// and the input pin connects to a different media type[/align]
[align=left]//[/align]
[align=left]// To sort out allocators:[/align]
[align=left]// The input is reconnected[/align]
[align=left]// if the output disconnects and the input was using a downstream allocator[/align]
[align=left]// The output pin calls SetAllocator to pass on a new allocator[/align]
[align=left]// if the output is connected and[/align]
[align=left]// if the input disconnects and the output was using an upstream allocator[/align]
[align=left]// if the input acquires an allocator different from the output one[/align]
[align=left]// and that new allocator is not R-O[/align]
[align=left]//[/align]
[align=left]// Data is copied (i.e. call getbuffer and copy the data before transforming it)[/align]
[align=left]// if the two allocators are different.[/align]
[align=left] [/align]
[align=left]// CHAINS of filters:[/align]
[align=left]// We sit between two filters (call them A and Z). We should finish up with the same allocator[/align]
[align=left]// on both of our pins and that should be the same one that A and Z would have agreed on if we[/align]
[align=left]// hadn't been in the way. Furthermore, it should not matter how many in-place transforms[/align]
[align=left]// are in the way. Let B, C, D... be in-place transforms ("us").[/align]
[align=left]// Here's how it goes:[/align]
[align=left]//[/align]
[align=left]// 1.[/align]
[align=left]// A connects to B. They agree on A's allocator.[/align]
[align=left]// A-a->B[/align]
[align=left]//[/align]
[align=left]// 2.[/align]
[align=left]// B connects to C. Same story. There is no point in a reconnect, but[/align]
[align=left]// B will request an input reconnect anyway.[/align]
[align=left]// A-a->B-a->C[/align]
[align=left]//[/align]
[align=left]// 3.[/align]
[align=left]// C connects to Z.[/align]
[align=left]// C insists on using A's allocator, but compromises by requesting a reconnect.[/align]
[align=left]// of C's input.[/align]
[align=left]// A-a->B-?->C-a->Z[/align]
[align=left]//[/align]
[align=left]// We now have pending reconnects on both A--->B and B--->C[/align]
[align=left]//[/align]
[align=left]// 4.[/align]
[align=left]// The A--->B link is reconnected.[/align]
[align=left]// A asks B for an allocator. B sees that it has a downstream connection so asks its downstream[/align]
[align=left]// input pin i.e. C's input pin for an allocator. C sees that it too has a downstream connection[/align]
[align=left]// so asks Z for an allocator.[/align]
[align=left]//[/align]
[align=left]// Even though Z's input pin is connected, it is being asked for an allocator.[/align]
[align=left]// It could refuse, in which case the chain is done and will use A's allocator[/align]
[align=left]// Alternatively, Z may supply one. A chooses either Z's or A's own one.[/align]
[align=left]// B's input pin gets NotifyAllocator called to tell it the decision and it[/align]
[align=left]// propagates this downstream by calling ReceiveAllocator on its output pin[/align]
[align=left]// which calls NotifyAllocator on the next input pin downstream etc.[/align]
[align=left]// If the choice is Z then it goes:[/align]
[align=left]// A-z->B-a->C-a->Z[/align]
[align=left]// A-z->B-z->C-a->Z[/align]
[align=left]// A-z->B-z->C-z->Z[/align]
[align=left]//[/align]
[align=left]// And that's IT!! Any further (essentially spurious) reconnects peter out with no change in[/align]
[align=left]// the chain.
[/align]
1. CTransInPlaceInputPin类[transip.h/transip.cpp]
派生自CTransformInputPin。
a) 成员变量:
CTransInPlaceFilter * const m_pTIPFilter; // our filter
BOOL m_bReadOnly; // incoming stream is read only
m_bReadOnly初始化为FALSE。
b) IMemInputPin接口函数
// Return our upstream allocator
// If the downstream filter has one then offer that (even if our own output pin is not using it yet.
// If the upstream filter chooses it then we will tell our output pin to ReceiveAllocator).
// Else if our output pin is using an allocator then offer that.
// ( This could mean offering the upstream filter his own allocator, it could mean offerring our own
// ) or it could mean offering the one from downstream
// Else fail to offer any allocator at all.
STDMETHODIMP GetAllocator(IMemAllocator ** ppAllocator);
{
CAutoLock cObjectLock(m_pLock);
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 {hr = CTransformInputPin::GetAllocator( ppAllocator );}
}
// get told which allocator the upstream output pin is actually going to use.
STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator, BOOL bReadOnly);
{
CAutoLock cObjectLock(m_pLock);
m_bReadOnly = bReadOnly;
// If we modify data then don't accept the allocator if it's the same as the output pin's allocator
// If our output is not connected just accept the allocator
// We're never going to use this allocator because when our
// output pin is connected we'll reconnect this pin
if (!m_pTIPFilter->OutputPin()->IsConnected()) {
return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly);}
// If the allocator is read-only and we're modifying data
// and the allocator is the same as the output pin's then reject
if (bReadOnly && m_pTIPFilter->m_bModifiesData) {
IMemAllocator *pOutputAllocator =
m_pTIPFilter->OutputPin()->PeekAllocator();
// Make sure we have an output allocator
if (pOutputAllocator == NULL) {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()->
GetAllocator(&pOutputAllocator);
if(FAILED(hr)) {hr = CreateMemoryAllocator(&pOutputAllocator);}
if (SUCCEEDED(hr)) {
m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator);
pOutputAllocator->Release(); }
}
if (pAllocator == pOutputAllocator) {hr = E_FAIL;}
else if(SUCCEEDED(hr)) {
// Must copy so set the allocator properties on the output
ALLOCATOR_PROPERTIES Props, Actual;
hr = pAllocator->GetProperties(&Props);
if (SUCCEEDED(hr)) {
hr = pOutputAllocator->SetProperties(&Props, &Actual);}
if (SUCCEEDED(hr)) {
if ( (Props.cBuffers > Actual.cBuffers)
|| (Props.cbBuffer > Actual.cbBuffer)
|| (Props.cbAlign > Actual.cbAlign)
) {hr = E_FAIL;}
}
// Set the allocator on the output pin
if (SUCCEEDED(hr)) {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
->NotifyAllocator( pOutputAllocator, FALSE ); }
}
} else {
hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()
->NotifyAllocator( pAllocator, bReadOnly );
if (SUCCEEDED(hr)) {
m_pTIPFilter->OutputPin()->SetAllocator( pAllocator );}
}
if (SUCCEEDED(hr)) {
// It's possible that the old and the new are the same thing.
// AddRef before release ensures that we don't unload it.
pAllocator->AddRef();
if( m_pAllocator != NULL ) m_pAllocator->Release();
m_pAllocator = pAllocator; // We have an allocator for the input pin
}
}
// Pass this on downstream if it ever gets called.
STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps);
{
if( m_pTIPFilter->m_pOutput->IsConnected() )
return m_pTIPFilter->OutputPin()
->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps );
else return E_NOTIMPL;
}
c) IPin接口函数以及CBasePin、CBaseInputPin继承函数
// Provide an enumerator for media types by getting one from downstream
STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );
{
// Can only pass through if connected
if( !m_pTIPFilter->m_pOutput->IsConnected() )
return VFW_E_NOT_CONNECTED;
return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes( ppEnum );
}
// Say whether media type is acceptable. - agree to anything if not connected,
// otherwise pass through to the downstream filter.
// This assumes that the filter does not change the media type.
HRESULT CheckMediaType(const CMediaType* pmt);
{
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
if( m_pTIPFilter->m_pOutput->IsConnected() )
return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt );
else return S_OK;
}
HRESULT CompleteConnect(IPin *pReceivePin);
{
HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin);
return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
}
d) 其他函数
// Allow the filter to see what allocator we have N.B. This does NOT AddRef
IMemAllocator * PeekAllocator() const{ return m_pAllocator; }
inline const BOOL ReadOnly() { return m_bReadOnly ; }
2. CTransInPlaceOutputPin类[transip.h/transip.cpp]
a) 成员变量
CTransInPlaceFilter * const m_pTIPFilter;
b) IPin接口函数,CBaseOutputPin函数
// Provide a media type enumerator. Get it from upstream.
STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );
{
if( ! m_pTIPFilter->m_pInput->IsConnected() )
return VFW_E_NOT_CONNECTED;
return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum );
}
// Say whether media type is acceptable. - agree to anything if not connected,
// otherwise pass through to the upstream filter.
HRESULT CheckMediaType(const CMediaType* pmt);
{
if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) {
if (*pmt == m_mt) {return S_OK;}
else {return VFW_E_TYPE_NOT_ACCEPTED;}
}
HRESULT hr = m_pTIPFilter->CheckInputType(pmt);
if( m_pTIPFilter->m_pInput->IsConnected() )
return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt );
else return S_OK;
}
HRESULT CompleteConnect(IPin *pReceivePin);
{
HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
}
c) 其他函数
// This just saves the allocator being used on the output pin
// Also called by input pin's GetAllocator()
void SetAllocator(IMemAllocator * pAllocator);
{
pAllocator->AddRef();
if (m_pAllocator) {m_pAllocator->Release();}
m_pAllocator = pAllocator;
}
IMemInputPin * ConnectedIMemInputPin(){ return m_pInputPin; }
IMemAllocator * PeekAllocator() const{ return m_pAllocator; }
3. CTransInPlaceFilter类[transip.h/transip.cpp]
a) 成员变量
bool m_bModifiesData; // Does this filter change the data?
m_bModifiesData被Constructor中bModifiesData初始化。
b) 新增加的virtual函数
virtual HRESULT Transform(IMediaSample *pSample) PURE;
c) 继承的IBaseFilter、CBaseFilter和CTransformFilter函数
// 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->Deliver(pSample);}
if (UsingDifferentAllocators()) {
pSample = Copy(pSample);}
// have the derived class transform the data
hr = Transform(pSample);
hr = m_pOutput->Deliver(pSample);
if (UsingDifferentAllocators()) {pSample->Release();}
}
HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin);
{
if (dir == PINDIR_OUTPUT) {
if( m_pInput->IsConnected() ) {
return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() );}
return NOERROR;
}
if( m_pOutput->IsConnected() ) {
if ( m_pInput->CurrentMediaType()
!= m_pOutput->CurrentMediaType()
) {
return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() );
}
}
}
// 必须override
HRESULT GetMediaType填充 ALLOCATOR_PROPERTIES,并调用pAlloc->SetProperties(pProperties, &Actual);
}
// [b]必须override
HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)
virtual CBasePin *GetPin(int n);
{
// Create an input pin if not already done
if (m_pInput == NULL) {
m_pInput = new CTransInPlaceInputPin( NAME("TransInPlace input pin")
, this // Owner filter
, &hr // Result code
, L"Input" // Pin name
);
if (m_pInput!=NULL && m_pOutput == NULL) {
m_pOutput = new CTransInPlaceOutputPin( NAME("TransInPlace output pin")
, this // Owner filter
, &hr // Result code
, L"Output" // Pin name
);
if (n == 0) {return m_pInput;}
else if (n==1) {return m_pOutput;}
else {return NULL;}
}[/b](int iPosition, CMediaType *pMediaType)
// This is called when we actually have to provide out own allocator.
HRESULT DecideBufferSize(IMemAllocator*, ALLOCATOR_PROPERTIES *);
{
// If we are connected upstream, get his views
if (m_pInput->IsConnected()) {
hr = InputPin()->PeekAllocator()->GetProperties(&Request); }
//
d) 其他函数
IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource);
{复制两个Sample}
CTransInPlaceInputPin *InputPin() const{return (CTransInPlaceInputPin *)m_pInput;}
CTransInPlaceOutputPin *OutputPin() const
{return (CTransInPlaceOutputPin *)m_pOutput;}
// Helper to see if the input and output types match
BOOL TypesMatch()
{return InputPin()->CurrentMediaType() ==OutputPin()->CurrentMediaType();}
// Are the input and output allocators different?
BOOL UsingDifferentAllocators() const
{return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator();}
[align=left]附上CTransInPlaceFilter的中的设计思路注释,这几个类应该是Base Classes中注释最多,最容易读的类了。
// How allocators are decided.[/align]
[align=left]// An in-place transform tries to do its work in someone else's buffers. It tries to persuade[/align]
[align=left]// the filters on either side to use the same allocator (and for that matter the same media type).[/align]
[align=left]// In desperation, if the downstream filter refuses to supply an allocator and the upstream filter[/align]
[align=left]// offers only a read-only one then it will provide an allocator. if the upstream filter insists[/align]
[align=left]// on a read-only allocator then the transform filter will (reluctantly) copy the data before[/align]
[align=left]// transforming it[/align]
[align=left]// In order to pass an allocator through it needs to remember the one it got from the first[/align]
[align=left]// connection to pass it on to the second one.[/align]
[align=left]// It is good if we can avoid insisting on a particular order of connection (There is a precedent[/align]
[align=left]// for insisting on the input being connected first. Insisting on the output being connected[/align]
[align=left]// first is not allowed. That would break RenderFile.)[/align]
[align=left]// The base pin classes (CBaseOutputPin and CBaseInputPin) both have a m_pAllocator member which[/align]
[align=left]// is used in places like CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive.[/align]
[align=left]// To avoid lots of extra overriding, we should keep these happy by using these pointers.[/align]
[align=left]// When each pin is connected, it will set the corresponding m_pAllocator and will have a single[/align]
[align=left]// ref-count on that allocator Refcounts are acquired by GetAllocator calls which return AddReffed[/align]
[align=left]// allocators and are released in one of:[/align]
[align=left]// CBaseInputPin::Disconnect[/align]
[align=left]// CBaseOutputPin::BreakConect[/align]
[align=left]// In each case m_pAllocator is set to NULL after the release, so this is the last chance to ever[/align]
[align=left]// release it. If there should ever be multiple refcounts associated with the same pointer, this[/align]
[align=left]// had better be cleared up before that happens. To avoid such problems, we'll stick with one[/align]
[align=left]// per pointer.[/align]
[align=left] [/align]
[align=left]// RECONNECTING and STATE CHANGES[/align]
[align=left]// Each pin could be disconnected, connected with a read-only allocator, connected with an upstream[/align]
[align=left]// read/write allocator, connected with an allocator from downstream or connected with its own[/align]
[align=left]// allocator. Five states for each pin gives a data space of 25 states.[/align]
[align=left] [/align]
[align=left]// Notation:[/align]
[align=left]// R/W == read/write[/align]
[align=left]// R-O == read-only[/align]
[align=left]// <input pin state> <output pin state> <comments>[/align]
[align=left]//[/align]
[align=left]// 00 means an unconnected pin.[/align]
[align=left]// <- means using a R/W allocator from the upstream filter[/align]
[align=left]// <= means using a R-O allocator from an upstream filter[/align]
[align=left]// || means using our own (R/W) allocator.[/align]
[align=left]// -> means using a R/W allocator from a downstream filter[/align]
[align=left]// (a R-O allocator from downstream is nonsense, it can't ever work).[/align]
[align=left]//[/align]
[align=left]// That makes 25 possible states. Some states are nonsense (two different[/align]
[align=left]// allocators from the same place). These are just an artifact of the notation.[/align]
[align=left]// <= <- Nonsense.[/align]
[align=left]// <- <= Nonsense[/align]
[align=left]// Some states are illegal (the output pin never accepts a R-O allocator):[/align]
[align=left]// 00 <= !! Error !![/align]
[align=left]// <= <= !! Error !![/align]
[align=left]// || <= !! Error !![/align]
[align=left]// -> <= !! Error !![/align]
[align=left]// Three states appears to be inaccessible:[/align]
[align=left]// -> || Inaccessible[/align]
[align=left]// || -> Inaccessible[/align]
[align=left]// || <- Inaccessible[/align]
[align=left]// Some states only ever occur as intermediates with a pending reconnect which[/align]
[align=left]// is guaranteed to finish in another state.[/align]
[align=left]// -> 00 ?? unstable goes to || 00[/align]
[align=left]// 00 <- ?? unstable goes to 00 ||[/align]
[align=left]// -> <- ?? unstable goes to -> ->[/align]
[align=left]// <- || ?? unstable goes to <- <-[/align]
[align=left]// <- -> ?? unstable goes to <- <-[/align]
[align=left]// And that leaves 11 possible resting states:[/align]
[align=left]// 1 00 00 Nothing connected.[/align]
[align=left]// 2 <- 00 Input pin connected.[/align]
[align=left]// 3 <= 00 Input pin connected using R-O allocator.[/align]
[align=left]// 4 || 00 Needs several state changes to get here.[/align]
[align=left]// 5 00 || Output pin connected using our allocator[/align]
[align=left]// 6 00 -> Downstream only connected[/align]
[align=left]// 7 || || Undesirable but can be forced upon us.[/align]
[align=left]// 8 <= || Copy forced. <= -> is preferable[/align]
[align=left]// 9 <= -> OK - forced to copy.[/align]
[align=left]// 10 <- <- Transform in place (ideal)[/align]
[align=left]// 11 -> -> Transform in place (ideal)[/align]
[align=left]//[/align]
[align=left]// The object of the exercise is to ensure that we finish up in states 10 or 11 whenever possible.[/align]
[align=left]// State 10 is only possible if the upstream filter has a R/W allocator (the AVI splitter[/align]
[align=left]// notoriously doesn't) and state 11 is only possible if the downstream filter does offer an[/align]
[align=left]// allocator.[/align]
[align=left]// The transition table (entries marked * go via a reconnect)[/align]
[align=left]// There are 8 possible transitions:[/align]
[align=left]// A: Connect upstream to filter with R-O allocator that insists on using it.[/align]
[align=left]// B: Connect upstream to filter with R-O allocator but chooses not to use it.[/align]
[align=left]// C: Connect upstream to filter with R/W allocator and insists on using it.[/align]
[align=left]// D: Connect upstream to filter with R/W allocator but chooses not to use it.[/align]
[align=left]// E: Connect downstream to a filter that offers an allocator[/align]
[align=left]// F: Connect downstream to a filter that does not offer an allocator[/align]
[align=left]// G: disconnect upstream[/align]
[align=left]// H: Disconnect downstream[/align]
[align=left]//[/align]
[align=left]// A B C D E F G H[/align]
[align=left]// ---------------------------------------------------------[/align]
[align=left]// 00 00 1 | 3 3 2 2 6 5 . . |1 00 00[/align]
[align=left]// <- 00 2 | . . . . *10/11 10 1 . |2 <- 00[/align]
[align=left]// <= 00 3 | . . . . *9/11 *7/8 1 . |3 <= 00[/align]
[align=left]// || 00 4 | . . . . *8 *7 1 . |4 || 00[/align]
[align=left]// 00 || 5 | 8 7 *10 7 . . . 1 |5 00 ||[/align]
[align=left]// 00 -> 6 | 9 11 *10 11 . . . 1 |6 00 ->[/align]
[align=left]// || || 7 | . . . . . . 5 4 |7 || ||[/align]
[align=left]// <= || 8 | . . . . . . 5 3 |8 <= ||[/align]
[align=left]// <= -> 9 | . . . . . . 6 3 |9 <= ->[/align]
[align=left]// <- <- 10| . . . . . . *5/6 2 |10 <- <-[/align]
[align=left]// -> -> 11| . . . . . . 6 *2/3 |11 -> ->[/align]
[align=left]// ---------------------------------------------------------[/align]
[align=left]// A B C D E F G H[/align]
[align=left]//[/align]
[align=left]// All these states are accessible without requiring any filter to change its behaviour but not[/align]
[align=left]// all transitions are accessible, // for instance a transition from state 4 to anywhere other[/align]
[align=left]// than state 8 requires that the upstream filter first offer a R-O allocator and then changes[/align]
[align=left]// its mind and offer R/W. This is NOT allowable - it leads to things like the output pin [/align]
[align=left]// getting a R/W allocator from upstream and then the input pin being told it can only have a[/align]
[align=left]// R-O one.[/align]
[align=left]// Note that you CAN change (say) the upstream filter for a different one, but only as a disconnect // connect, not as a Reconnect. (Exercise for the reader is to see how you get into state 4).[/align]
[align=left]//[/align]
[align=left]// The reconnection stuff goes as follows (some of the cases shown here as "no reconnect" may[/align]
[align=left]// get one to finalise media type - an old story). If there is a reconnect where it says "no[/align]
[align=left]// reconnect" here then the reconnection must not change the allocator choice.[/align]
[align=left]// state 2: <- 00 transition E <- <- case C <- <- (no change)[/align]
[align=left]// case D -> <- and then to -> ->[/align]
[align=left]//[/align]
[align=left]// state 2: <- 00 transition F <- <- (no reconnect)[/align]
[align=left]//[/align]
[align=left]// state 3: <= 00 transition E <= -> case A <= -> (no change)[/align]
[align=left]// case B -> ->[/align]
[align=left]// transition F <= || case A <= || (no change)[/align]
[align=left]// case B || ||[/align]
[align=left]//[/align]
[align=left]// state 4: || 00 transition E || || case B -> || and then all cases to -> ->[/align]
[align=left]// F || || case B || || (no change)[/align]
[align=left]//[/align]
[align=left]// state 5: 00 || transition A <= || (no reconnect)[/align]
[align=left]// B || || (no reconnect)[/align]
[align=left]// C <- || all cases <- <-[/align]
[align=left]// D || || (unfortunate, but upstream's choice)[/align]
[align=left]//[/align]
[align=left]// state 6: 00 -> transition A <= -> (no reconnect)[/align]
[align=left]// B -> -> (no reconnect)[/align]
[align=left]// C <- -> all cases <- <-[/align]
[align=left]// D -> -> (no reconnect)[/align]
[align=left]//[/align]
[align=left]// state 10:<- <- transition G 00 <- case E 00 ->[/align]
[align=left]// case F 00 ||[/align]
[align=left]//[/align]
[align=left]// state 11:-> -> transition H -> 00 case A <= 00 (schizo)[/align]
[align=left]// case B <= 00[/align]
[align=left]// case C <- 00 (schizo)[/align]
[align=left]// case D <- 00[/align]
[align=left]//[/align]
[align=left]// The Rules:[/align]
[align=left]// To sort out media types:[/align]
[align=left]// The input is reconnected[/align]
[align=left]// if the input pin is connected and the output pin connects[/align]
[align=left]// The output is reconnected[/align]
[align=left]// If the output pin is connected[/align]
[align=left]// and the input pin connects to a different media type[/align]
[align=left]//[/align]
[align=left]// To sort out allocators:[/align]
[align=left]// The input is reconnected[/align]
[align=left]// if the output disconnects and the input was using a downstream allocator[/align]
[align=left]// The output pin calls SetAllocator to pass on a new allocator[/align]
[align=left]// if the output is connected and[/align]
[align=left]// if the input disconnects and the output was using an upstream allocator[/align]
[align=left]// if the input acquires an allocator different from the output one[/align]
[align=left]// and that new allocator is not R-O[/align]
[align=left]//[/align]
[align=left]// Data is copied (i.e. call getbuffer and copy the data before transforming it)[/align]
[align=left]// if the two allocators are different.[/align]
[align=left] [/align]
[align=left]// CHAINS of filters:[/align]
[align=left]// We sit between two filters (call them A and Z). We should finish up with the same allocator[/align]
[align=left]// on both of our pins and that should be the same one that A and Z would have agreed on if we[/align]
[align=left]// hadn't been in the way. Furthermore, it should not matter how many in-place transforms[/align]
[align=left]// are in the way. Let B, C, D... be in-place transforms ("us").[/align]
[align=left]// Here's how it goes:[/align]
[align=left]//[/align]
[align=left]// 1.[/align]
[align=left]// A connects to B. They agree on A's allocator.[/align]
[align=left]// A-a->B[/align]
[align=left]//[/align]
[align=left]// 2.[/align]
[align=left]// B connects to C. Same story. There is no point in a reconnect, but[/align]
[align=left]// B will request an input reconnect anyway.[/align]
[align=left]// A-a->B-a->C[/align]
[align=left]//[/align]
[align=left]// 3.[/align]
[align=left]// C connects to Z.[/align]
[align=left]// C insists on using A's allocator, but compromises by requesting a reconnect.[/align]
[align=left]// of C's input.[/align]
[align=left]// A-a->B-?->C-a->Z[/align]
[align=left]//[/align]
[align=left]// We now have pending reconnects on both A--->B and B--->C[/align]
[align=left]//[/align]
[align=left]// 4.[/align]
[align=left]// The A--->B link is reconnected.[/align]
[align=left]// A asks B for an allocator. B sees that it has a downstream connection so asks its downstream[/align]
[align=left]// input pin i.e. C's input pin for an allocator. C sees that it too has a downstream connection[/align]
[align=left]// so asks Z for an allocator.[/align]
[align=left]//[/align]
[align=left]// Even though Z's input pin is connected, it is being asked for an allocator.[/align]
[align=left]// It could refuse, in which case the chain is done and will use A's allocator[/align]
[align=left]// Alternatively, Z may supply one. A chooses either Z's or A's own one.[/align]
[align=left]// B's input pin gets NotifyAllocator called to tell it the decision and it[/align]
[align=left]// propagates this downstream by calling ReceiveAllocator on its output pin[/align]
[align=left]// which calls NotifyAllocator on the next input pin downstream etc.[/align]
[align=left]// If the choice is Z then it goes:[/align]
[align=left]// A-z->B-a->C-a->Z[/align]
[align=left]// A-z->B-z->C-a->Z[/align]
[align=left]// A-z->B-z->C-z->Z[/align]
[align=left]//[/align]
[align=left]// And that's IT!! Any further (essentially spurious) reconnects peter out with no change in[/align]
[align=left]// the chain.
[/align]
相关文章推荐
- DirectShow 学习(七) CTransInPlaceFilter及相关联Pin类的源代码解析
- DirectShow 学习(七) CTransInPlaceFilter及相关联Pin类的源代码解析
- DirectShow 学习(六): CTransfromFilter及相关联Pin类的源代码解析(转载)
- DirectShow 学习(六) CTransfromFilter及相关联Pin类的源代码解析
- DirectShow 学习(三) CBaseFilter类源代码解析(转载)
- DirectShow学习(八): CBaseRender类及相应Pin类的源代码分析(转载)
- DirectShow 学习(六) CTransfromFilter及相关联Pin类的源代码解析
- DirectShow学习(二)CBasePin类、CBaseOutputPin类和CBaseInputPin类源代码解析
- DirectShow学习(八): CBaseRender类及相应Pin类的源代码分析
- DirectShow 学习(五) CSource类和其一个具体实现例子的源代码分析(转载)
- 转载 rageliu 的 DirectShow中写push模式的source filter流程 + 源代码(内附详细注释)
- DirectShow学习(八): CBaseRender类及相应Pin类的源代码分析
- DirectShow 学习(三) CBaseFilter类源代码解析
- DirectShow学习(二)CBasePin类、CBaseOutputPin类和CBaseInputPin类源代码解析(转载)
- DirectShow中写push模式的source filter流程 + 源代码(内附详细注释)
- Android 进阶学习:事件分发机制全然解析,带你从源代码的角度彻底理解(上)
- A very simple CTransInPlaceFilter example
- lucene (42)源代码学习之FST(Finite State Transducer)在SynonymFilter中的实现思想
- MPEG-2 TS学习(九)tsfilter源码阅读(6)解析音视频数据
- DirectShow学习--Filter的开发(1)