DirectShow 学习(七) CTransInPlaceFilter及相关联Pin类的源代码解析

类别:软件工程 点击:0 评论:0 推荐:

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(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);  }
// 填充 ALLOCATOR_PROPERTIES,并调用pAlloc->SetProperties(pProperties, &Actual);
}
// 必须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;}
}

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

 

附上CTransInPlaceFilter的中的设计思路注释,这几个类应该是Base Classes中注释最多,最容易读的类了。
// How allocators are decided.

// An in-place transform tries to do its work in someone else's buffers. It tries to persuade

// the filters on either side to use the same allocator (and for that matter the same media type).

// In desperation, if the downstream filter refuses to supply an allocator and the upstream filter

// offers only a read-only one then it will provide an allocator. if the upstream filter insists

// on a read-only allocator then the transform filter will (reluctantly) copy the data before

// transforming it

// In order to pass an allocator through it needs to remember the one it got from the first

// connection to pass it on to the second one.

// It is good if we can avoid insisting on a particular order of connection (There is a precedent

// for insisting on the input being connected first.  Insisting on the output being connected

// first is not allowed.  That would break RenderFile.)

// The base pin classes (CBaseOutputPin and CBaseInputPin) both have a m_pAllocator member which

// is used in places like CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive.

// To avoid lots of extra overriding, we should keep these happy by using these pointers.

// When each pin is connected, it will set the corresponding m_pAllocator and will have a single

// ref-count on that allocator Refcounts are acquired by GetAllocator calls which return AddReffed

// allocators and are released in one of:

//     CBaseInputPin::Disconnect

//     CBaseOutputPin::BreakConect

// In each case m_pAllocator is set to NULL after the release, so this is the last chance to ever

// release it.  If there should ever be multiple refcounts associated with the same pointer, this

// had better be cleared up before that happens.  To avoid such problems, we'll stick with one

// per pointer.

 

// RECONNECTING and STATE CHANGES

// Each pin could be disconnected, connected with a read-only allocator, connected with an upstream

// read/write allocator, connected with an allocator from downstream or connected with its own

// allocator. Five states for each pin gives a data space of 25 states.

 

// Notation:

// R/W == read/write

// R-O == read-only

// <input pin state> <output pin state> <comments>

//

// 00 means an unconnected pin.

// <- means using a R/W allocator from the upstream filter

// <= means using a R-O allocator from an upstream filter

// || means using our own (R/W) allocator.

// -> means using a R/W allocator from a downstream filter

//    (a R-O allocator from downstream is nonsense, it can't ever work).

//

// That makes 25 possible states.  Some states are nonsense (two different

// allocators from the same place).  These are just an artifact of the notation.

//        <=  <-  Nonsense.

//        <-  <=  Nonsense

// Some states are illegal (the output pin never accepts a R-O allocator):

//        00  <=  !! Error !!

//        <=  <=  !! Error !!

//        ||  <=  !! Error !!

//        ->  <=  !! Error !!

// Three states appears to be inaccessible:

//        ->  ||  Inaccessible

//        ||  ->  Inaccessible

//        ||  <-  Inaccessible

// Some states only ever occur as intermediates with a pending reconnect which

// is guaranteed to finish in another state.

//        ->  00  ?? unstable goes to || 00

//        00  <-  ?? unstable goes to 00 ||

//        ->  <-  ?? unstable goes to -> ->

//        <-  ||  ?? unstable goes to <- <-

//        <-  ->  ?? unstable goes to <- <-

// And that leaves 11 possible resting states:

// 1      00  00  Nothing connected.

// 2      <-  00  Input pin connected.

// 3      <=  00  Input pin connected using R-O allocator.

// 4      ||  00  Needs several state changes to get here.

// 5      00  ||  Output pin connected using our allocator

// 6      00  ->  Downstream only connected

// 7      ||  ||  Undesirable but can be forced upon us.

// 8      <=  ||  Copy forced.  <=  -> is preferable

// 9      <=  ->  OK - forced to copy.

// 10     <-  <-  Transform in place (ideal)

// 11     ->  ->  Transform in place (ideal)

//

// The object of the exercise is to ensure that we finish up in states 10 or 11 whenever possible.

//  State 10 is only possible if the upstream filter has a R/W allocator (the AVI splitter

// notoriously doesn't) and state 11 is only possible if the downstream filter does offer an

// allocator.

// The transition table (entries marked * go via a reconnect)

// There are 8 possible transitions:

// A: Connect upstream to filter with R-O allocator that insists on using it.

// B: Connect upstream to filter with R-O allocator but chooses not to use it.

// C: Connect upstream to filter with R/W allocator and insists on using it.

// D: Connect upstream to filter with R/W allocator but chooses not to use it.

// E: Connect downstream to a filter that offers an allocator

// F: Connect downstream to a filter that does not offer an allocator

// G: disconnect upstream

// H: Disconnect downstream

//

//            A      B      C      D      E      F      G      H

//           ---------------------------------------------------------

// 00  00 1 | 3      3      2      2      6      5      .      .      |1  00  00

// <-  00 2 | .      .      .      .      *10/11 10     1      .      |2  <-  00

// <=  00 3 | .      .      .      .      *9/11  *7/8   1      .      |3  <=  00

// ||  00 4 | .      .      .      .      *8     *7     1      .      |4  ||  00

// 00  || 5 | 8      7      *10    7      .      .      .      1      |5  00  ||

// 00  -> 6 | 9      11     *10    11     .      .      .      1      |6  00  ->

// ||  || 7 | .      .      .      .      .      .      5      4      |7  ||  ||

// <=  || 8 | .      .      .      .      .      .      5      3      |8  <=  ||

// <=  -> 9 | .      .      .      .      .      .      6      3      |9  <=  ->

// <-  <- 10| .      .      .      .      .      .      *5/6   2      |10 <-  <-

// ->  -> 11| .      .      .      .      .      .      6      *2/3   |11 ->  ->

//           ---------------------------------------------------------

//            A      B      C      D      E      F      G      H

//

// All these states are accessible without requiring any filter to change its behaviour but not

// all transitions are accessible, // for instance a transition from state 4 to anywhere other

// than state 8 requires that the upstream filter first offer a R-O allocator and then changes

// its mind and offer R/W.  This is NOT allowable - it leads to things like the output pin

// getting a R/W allocator from upstream and then the input pin being told it can only have a

// R-O one.

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

//

// The reconnection stuff goes as follows (some of the cases shown here as "no reconnect" may

// get one to finalise media type - an old story).  If there is a reconnect where it says "no

// reconnect" here then the reconnection must not change the allocator choice.

// state 2: <- 00 transition E <- <- case C <- <- (no change)

//                                   case D -> <- and then to -> ->

//

// state 2: <- 00 transition F <- <- (no reconnect)

//

// state 3: <= 00 transition E <= -> case A <= -> (no change)

//                                   case B -> ->

//                transition F <= || case A <= || (no change)

//                                   case B || ||

//

// state 4: || 00 transition E || || case B -> || and then all cases to -> ->

//                           F || || case B || || (no change)

//

// state 5: 00 || transition A <= || (no reconnect)

//                           B || || (no reconnect)

//                           C <- || all cases     <- <-

//                           D || || (unfortunate, but upstream's choice)

//

// state 6: 00 -> transition A <= -> (no reconnect)

//                           B -> -> (no reconnect)

//                           C <- -> all cases <- <-

//                           D -> -> (no reconnect)

//

// state 10:<- <- transition G 00 <- case E 00 ->

//                                   case F 00 ||

//

// state 11:-> -> transition H -> 00 case A <= 00 (schizo)

//                                   case B <= 00

//                                   case C <- 00 (schizo)

//                                   case D <- 00

//

// The Rules:

// To sort out media types:

// The input is reconnected

//    if the input pin is connected and the output pin connects

// The output is reconnected

//    If the output pin is connected

//    and the input pin connects to a different media type

//

// To sort out allocators:

// The input is reconnected

//    if the output disconnects and the input was using a downstream allocator

// The output pin calls SetAllocator to pass on a new allocator

//    if the output is connected and

//       if the input disconnects and the output was using an upstream allocator

//       if the input acquires an allocator different from the output one

//          and that new allocator is not R-O

//

// Data is copied (i.e. call getbuffer and copy the data before transforming it)

//    if the two allocators are different.

 

// CHAINS of filters:

// We sit between two filters (call them A and Z).  We should finish up with the same allocator

// on both of our pins and that should be the same one that A and Z would have agreed on if we

// hadn't been in the way.  Furthermore, it should not matter how many in-place transforms

// are in the way.  Let B, C, D... be in-place transforms ("us").

// Here's how it goes:

//

// 1.

// A connects to B.  They agree on A's allocator.

//   A-a->B

//

// 2.

// B connects to C.  Same story. There is no point in a reconnect, but

// B will request an input reconnect anyway.

//   A-a->B-a->C

//

// 3.

// C connects to Z.

// C insists on using A's allocator, but compromises by requesting a reconnect.

// of C's input.

//   A-a->B-?->C-a->Z

//

// We now have pending reconnects on both A--->B and B--->C

//

// 4.

// The A--->B link is reconnected.

// A asks B for an allocator.  B sees that it has a downstream connection so asks its downstream

// input pin i.e. C's input pin for an allocator.  C sees that it too has a downstream connection

// so asks Z for an allocator.

//

// Even though Z's input pin is connected, it is being asked for an allocator.

// It could refuse, in which case the chain is done and will use A's allocator

// Alternatively, Z may supply one.  A chooses either Z's or A's own one.

// B's input pin gets NotifyAllocator called to tell it the decision and it

// propagates this downstream by calling ReceiveAllocator on its output pin

// which calls NotifyAllocator on the next input pin downstream etc.

// If the choice is Z then it goes:

//   A-z->B-a->C-a->Z

//   A-z->B-z->C-a->Z

//   A-z->B-z->C-z->Z

//

// And that's IT!!  Any further (essentially spurious) reconnects peter out with no change in

// the chain.
前一篇:(六) CTransfromFilter及相关联Pin类的源代码解析

本文地址:http://com.8s8s.com/it/it32798.htm