975 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			975 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | //------------------------------------------------------------------------------
 | ||
|  | // File: TransIP.cpp
 | ||
|  | //
 | ||
|  | // Desc: DirectShow base classes - implements class for simple Transform-
 | ||
|  | //       In-Place filters such as audio.
 | ||
|  | //
 | ||
|  | // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
 | ||
|  | //------------------------------------------------------------------------------
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // 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.
 | ||
|  | 
 | ||
|  | #include <streams.h>
 | ||
|  | #include <measure.h>
 | ||
|  | #include <transip.h>
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // =================================================================
 | ||
|  | // Implements the CTransInPlaceFilter class
 | ||
|  | // =================================================================
 | ||
|  | 
 | ||
|  | CTransInPlaceFilter::CTransInPlaceFilter | ||
|  |    ( __in_opt LPCTSTR    pName, | ||
|  |      __inout_opt LPUNKNOWN  pUnk, | ||
|  |      REFCLSID   clsid, | ||
|  |      __inout HRESULT   *phr, | ||
|  |      bool       bModifiesData | ||
|  |    ) | ||
|  |    : CTransformFilter(pName, pUnk, clsid), | ||
|  |      m_bModifiesData(bModifiesData) | ||
|  | { | ||
|  | #ifdef PERF
 | ||
|  |     RegisterPerfId(); | ||
|  | #endif //  PERF
 | ||
|  | 
 | ||
|  | } // constructor
 | ||
|  | 
 | ||
|  | #ifdef UNICODE
 | ||
|  | CTransInPlaceFilter::CTransInPlaceFilter | ||
|  |    ( __in_opt LPCSTR  pName, | ||
|  |      __inout_opt LPUNKNOWN  pUnk, | ||
|  |      REFCLSID   clsid, | ||
|  |      __inout HRESULT   *phr, | ||
|  |      bool       bModifiesData | ||
|  |    ) | ||
|  |    : CTransformFilter(pName, pUnk, clsid), | ||
|  |      m_bModifiesData(bModifiesData) | ||
|  | { | ||
|  | #ifdef PERF
 | ||
|  |     RegisterPerfId(); | ||
|  | #endif //  PERF
 | ||
|  | 
 | ||
|  | } // constructor
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | // return a non-addrefed CBasePin * for the user to addref if he holds onto it
 | ||
|  | // for longer than his pointer to us. We create the pins dynamically when they
 | ||
|  | // are asked for rather than in the constructor. This is because we want to
 | ||
|  | // give the derived class an oppportunity to return different pin objects
 | ||
|  | 
 | ||
|  | // As soon as any pin is needed we create both (this is different from the
 | ||
|  | // usual transform filter) because enumerators, allocators etc are passed
 | ||
|  | // through from one pin to another and it becomes very painful if the other
 | ||
|  | // pin isn't there.  If we fail to create either pin we ensure we fail both.
 | ||
|  | 
 | ||
|  | CBasePin * | ||
|  | CTransInPlaceFilter::GetPin(int n) | ||
|  | { | ||
|  |     HRESULT hr = S_OK; | ||
|  | 
 | ||
|  |     // 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
 | ||
|  |                                             ); | ||
|  | 
 | ||
|  |         // Constructor for CTransInPlaceInputPin can't fail
 | ||
|  |         ASSERT(SUCCEEDED(hr)); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Create an output pin if not already done
 | ||
|  | 
 | ||
|  |     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
 | ||
|  |                                               ); | ||
|  | 
 | ||
|  |         // a failed return code should delete the object
 | ||
|  | 
 | ||
|  |         ASSERT(SUCCEEDED(hr)); | ||
|  |         if (m_pOutput == NULL) { | ||
|  |             delete m_pInput; | ||
|  |             m_pInput = NULL; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // Return the appropriate pin
 | ||
|  | 
 | ||
|  |     ASSERT (n>=0 && n<=1); | ||
|  |     if (n == 0) { | ||
|  |         return m_pInput; | ||
|  |     } else if (n==1) { | ||
|  |         return m_pOutput; | ||
|  |     } else { | ||
|  |         return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  | } // GetPin
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // dir is the direction of our pin.
 | ||
|  | // pReceivePin is the pin we are connecting to.
 | ||
|  | HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin) | ||
|  | { | ||
|  |     UNREFERENCED_PARAMETER(pReceivePin); | ||
|  |     ASSERT(m_pInput); | ||
|  |     ASSERT(m_pOutput); | ||
|  | 
 | ||
|  |     // if we are not part of a graph, then don't indirect the pointer
 | ||
|  |     // this probably prevents use of the filter without a filtergraph
 | ||
|  |     if (!m_pGraph) { | ||
|  |         return VFW_E_NOT_IN_GRAPH; | ||
|  |     } | ||
|  | 
 | ||
|  |     // Always reconnect the input to account for buffering changes
 | ||
|  |     //
 | ||
|  |     // Because we don't get to suggest a type on ReceiveConnection
 | ||
|  |     // we need another way of making sure the right type gets used.
 | ||
|  |     //
 | ||
|  |     // One way would be to have our EnumMediaTypes return our output
 | ||
|  |     // connection type first but more deterministic and simple is to
 | ||
|  |     // call ReconnectEx passing the type we want to reconnect with
 | ||
|  |     // via the base class ReconeectPin method.
 | ||
|  | 
 | ||
|  |     if (dir == PINDIR_OUTPUT) { | ||
|  |         if( m_pInput->IsConnected() ) { | ||
|  |             return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() ); | ||
|  |         } | ||
|  |         return NOERROR; | ||
|  |     } | ||
|  | 
 | ||
|  |     ASSERT(dir == PINDIR_INPUT); | ||
|  | 
 | ||
|  |     // Reconnect output if necessary
 | ||
|  | 
 | ||
|  |     if( m_pOutput->IsConnected() ) { | ||
|  | 
 | ||
|  |         if (  m_pInput->CurrentMediaType() | ||
|  |            != m_pOutput->CurrentMediaType() | ||
|  |            ) { | ||
|  |             return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() ); | ||
|  |         } | ||
|  |     } | ||
|  |     return NOERROR; | ||
|  | 
 | ||
|  | } // ComnpleteConnect
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //
 | ||
|  | // DecideBufferSize
 | ||
|  | //
 | ||
|  | // Tell the output pin's allocator what size buffers we require.
 | ||
|  | // *pAlloc will be the allocator our output pin is using.
 | ||
|  | //
 | ||
|  | 
 | ||
|  | HRESULT CTransInPlaceFilter::DecideBufferSize | ||
|  |             ( IMemAllocator *pAlloc | ||
|  |             , __inout ALLOCATOR_PROPERTIES *pProperties | ||
|  |             ) | ||
|  | { | ||
|  |     ALLOCATOR_PROPERTIES Request, Actual; | ||
|  |     HRESULT hr; | ||
|  | 
 | ||
|  |     // If we are connected upstream, get his views
 | ||
|  |     if (m_pInput->IsConnected()) { | ||
|  |         // Get the input pin allocator, and get its size and count.
 | ||
|  |         // we don't care about his alignment and prefix.
 | ||
|  | 
 | ||
|  |         hr = InputPin()->PeekAllocator()->GetProperties(&Request); | ||
|  |         if (FAILED(hr)) { | ||
|  |             // Input connected but with a secretive allocator - enough!
 | ||
|  |             return hr; | ||
|  |         } | ||
|  |     } else { | ||
|  |         // Propose one byte
 | ||
|  |         // If this isn't enough then when the other pin does get connected
 | ||
|  |         // we can revise it.
 | ||
|  |         ZeroMemory(&Request, sizeof(Request)); | ||
|  |         Request.cBuffers = 1; | ||
|  |         Request.cbBuffer = 1; | ||
|  |     } | ||
|  | 
 | ||
|  | 
 | ||
|  |     DbgLog((LOG_MEMORY,1,TEXT("Setting Allocator Requirements"))); | ||
|  |     DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d"), | ||
|  |            Request.cBuffers, Request.cbBuffer)); | ||
|  | 
 | ||
|  |     // Pass the allocator requirements to our output side
 | ||
|  |     // but do a little sanity checking first or we'll just hit
 | ||
|  |     // asserts in the allocator.
 | ||
|  | 
 | ||
|  |     pProperties->cBuffers = Request.cBuffers; | ||
|  |     pProperties->cbBuffer = Request.cbBuffer; | ||
|  |     pProperties->cbAlign = Request.cbAlign; | ||
|  |     if (pProperties->cBuffers<=0) {pProperties->cBuffers = 1; } | ||
|  |     if (pProperties->cbBuffer<=0) {pProperties->cbBuffer = 1; } | ||
|  |     hr = pAlloc->SetProperties(pProperties, &Actual); | ||
|  | 
 | ||
|  |     if (FAILED(hr)) { | ||
|  |         return hr; | ||
|  |     } | ||
|  | 
 | ||
|  |     DbgLog((LOG_MEMORY,1,TEXT("Obtained Allocator Requirements"))); | ||
|  |     DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d, Alignment %d"), | ||
|  |            Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign)); | ||
|  | 
 | ||
|  |     // Make sure we got the right alignment and at least the minimum required
 | ||
|  | 
 | ||
|  |     if (  (Request.cBuffers > Actual.cBuffers) | ||
|  |        || (Request.cbBuffer > Actual.cbBuffer) | ||
|  |        || (Request.cbAlign  > Actual.cbAlign) | ||
|  |        ) { | ||
|  |         return E_FAIL; | ||
|  |     } | ||
|  |     return NOERROR; | ||
|  | 
 | ||
|  | } // DecideBufferSize
 | ||
|  | 
 | ||
|  | //
 | ||
|  | // Copy
 | ||
|  | //
 | ||
|  | // return a pointer to an identical copy of pSample
 | ||
|  | __out_opt IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource) | ||
|  | { | ||
|  |     IMediaSample * pDest; | ||
|  | 
 | ||
|  |     HRESULT hr; | ||
|  |     REFERENCE_TIME tStart, tStop; | ||
|  |     const BOOL bTime = S_OK == pSource->GetTime( &tStart, &tStop); | ||
|  | 
 | ||
|  |     // this may block for an indeterminate amount of time
 | ||
|  |     hr = OutputPin()->PeekAllocator()->GetBuffer( | ||
|  |               &pDest | ||
|  |               , bTime ? &tStart : NULL | ||
|  |               , bTime ? &tStop : NULL | ||
|  |               , m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0 | ||
|  |               ); | ||
|  | 
 | ||
|  |     if (FAILED(hr)) { | ||
|  |         return NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     ASSERT(pDest); | ||
|  |     IMediaSample2 *pSample2; | ||
|  |     if (SUCCEEDED(pDest->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) { | ||
|  |         HRESULT hrProps = pSample2->SetProperties( | ||
|  |             FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer), | ||
|  |             (PBYTE)m_pInput->SampleProps()); | ||
|  |         pSample2->Release(); | ||
|  |         if (FAILED(hrProps)) { | ||
|  |             pDest->Release(); | ||
|  |             return NULL; | ||
|  |         } | ||
|  |     } else { | ||
|  |         if (bTime) { | ||
|  |             pDest->SetTime(&tStart, &tStop); | ||
|  |         } | ||
|  | 
 | ||
|  |         if (S_OK == pSource->IsSyncPoint()) { | ||
|  |             pDest->SetSyncPoint(TRUE); | ||
|  |         } | ||
|  |         if (S_OK == pSource->IsDiscontinuity() || m_bSampleSkipped) { | ||
|  |             pDest->SetDiscontinuity(TRUE); | ||
|  |         } | ||
|  |         if (S_OK == pSource->IsPreroll()) { | ||
|  |             pDest->SetPreroll(TRUE); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Copy the media type
 | ||
|  |         AM_MEDIA_TYPE *pMediaType; | ||
|  |         if (S_OK == pSource->GetMediaType(&pMediaType)) { | ||
|  |             pDest->SetMediaType(pMediaType); | ||
|  |             DeleteMediaType( pMediaType ); | ||
|  |         } | ||
|  | 
 | ||
|  |     } | ||
|  | 
 | ||
|  |     m_bSampleSkipped = FALSE; | ||
|  | 
 | ||
|  |     // Copy the sample media times
 | ||
|  |     REFERENCE_TIME TimeStart, TimeEnd; | ||
|  |     if (pSource->GetMediaTime(&TimeStart,&TimeEnd) == NOERROR) { | ||
|  |         pDest->SetMediaTime(&TimeStart,&TimeEnd); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Copy the actual data length and the actual data.
 | ||
|  |     { | ||
|  |         const long lDataLength = pSource->GetActualDataLength(); | ||
|  |         if (FAILED(pDest->SetActualDataLength(lDataLength))) { | ||
|  |             pDest->Release(); | ||
|  |             return NULL; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Copy the sample data
 | ||
|  |         { | ||
|  |             BYTE *pSourceBuffer, *pDestBuffer; | ||
|  |             long lSourceSize  = pSource->GetSize(); | ||
|  |             long lDestSize = pDest->GetSize(); | ||
|  | 
 | ||
|  |             ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength); | ||
|  | 
 | ||
|  |             if (FAILED(pSource->GetPointer(&pSourceBuffer)) || | ||
|  |                 FAILED(pDest->GetPointer(&pDestBuffer)) || | ||
|  |                 lDestSize < lDataLength || | ||
|  |                 lDataLength < 0) { | ||
|  |                 pDest->Release(); | ||
|  |                 return NULL; | ||
|  |             } | ||
|  |             ASSERT(lDestSize == 0 || pSourceBuffer != NULL && pDestBuffer != NULL); | ||
|  | 
 | ||
|  |             CopyMemory( (PVOID) pDestBuffer, (PVOID) pSourceBuffer, lDataLength ); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return pDest; | ||
|  | 
 | ||
|  | } // Copy
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // override this to customize the transform process
 | ||
|  | 
 | ||
|  | HRESULT | ||
|  | CTransInPlaceFilter::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); | ||
|  |     } | ||
|  |     HRESULT hr; | ||
|  | 
 | ||
|  |     // Start timing the TransInPlace (if PERF is defined)
 | ||
|  |     MSR_START(m_idTransInPlace); | ||
|  | 
 | ||
|  |     if (UsingDifferentAllocators()) { | ||
|  | 
 | ||
|  |         // We have to copy the data.
 | ||
|  | 
 | ||
|  |         pSample = Copy(pSample); | ||
|  | 
 | ||
|  |         if (pSample==NULL) { | ||
|  |             MSR_STOP(m_idTransInPlace); | ||
|  |             return E_UNEXPECTED; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // have the derived class transform the data
 | ||
|  |     hr = Transform(pSample); | ||
|  | 
 | ||
|  |     // Stop the clock and log it (if PERF is defined)
 | ||
|  |     MSR_STOP(m_idTransInPlace); | ||
|  | 
 | ||
|  |     if (FAILED(hr)) { | ||
|  |         DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace"))); | ||
|  |         if (UsingDifferentAllocators()) { | ||
|  |             pSample->Release(); | ||
|  |         } | ||
|  |         return hr; | ||
|  |     } | ||
|  | 
 | ||
|  |     // 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->Deliver(pSample); | ||
|  |     } else { | ||
|  |         //  But it would be an error to return this private workaround
 | ||
|  |         //  to the caller ...
 | ||
|  |         if (S_FALSE == hr) { | ||
|  |             // 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.
 | ||
|  |             m_bSampleSkipped = TRUE; | ||
|  |             if (!m_bQualityChanged) { | ||
|  |                 NotifyEvent(EC_QUALITY_CHANGE,0,0); | ||
|  |                 m_bQualityChanged = TRUE; | ||
|  |             } | ||
|  |             hr = NOERROR; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // release the output buffer. If the connected pin still needs it,
 | ||
|  |     // it will have addrefed it itself.
 | ||
|  |     if (UsingDifferentAllocators()) { | ||
|  |         pSample->Release(); | ||
|  |     } | ||
|  | 
 | ||
|  |     return hr; | ||
|  | 
 | ||
|  | } // Receive
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // =================================================================
 | ||
|  | // Implements the CTransInPlaceInputPin class
 | ||
|  | // =================================================================
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // constructor
 | ||
|  | 
 | ||
|  | CTransInPlaceInputPin::CTransInPlaceInputPin | ||
|  |     ( __in_opt LPCTSTR             pObjectName | ||
|  |     , __inout CTransInPlaceFilter *pFilter | ||
|  |     , __inout HRESULT             *phr | ||
|  |     , __in_opt LPCWSTR             pName | ||
|  |     ) | ||
|  |     : CTransformInputPin(pObjectName, | ||
|  |                          pFilter, | ||
|  |                          phr, | ||
|  |                          pName) | ||
|  |     , m_bReadOnly(FALSE) | ||
|  |     , m_pTIPFilter(pFilter) | ||
|  | { | ||
|  |     DbgLog((LOG_TRACE, 2 | ||
|  |            , TEXT("CTransInPlaceInputPin::CTransInPlaceInputPin"))); | ||
|  | 
 | ||
|  | } // constructor
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // =================================================================
 | ||
|  | // Implements IMemInputPin interface
 | ||
|  | // =================================================================
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // 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 CTransInPlaceInputPin::GetAllocator(__deref_out IMemAllocator ** ppAllocator) | ||
|  | { | ||
|  |     CheckPointer(ppAllocator,E_POINTER); | ||
|  |     ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *)); | ||
|  |     CAutoLock cObjectLock(m_pLock); | ||
|  | 
 | ||
|  |     HRESULT hr; | ||
|  | 
 | ||
|  |     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 { | ||
|  |         //  Help upstream filter (eg TIP filter which is having to do a copy)
 | ||
|  |         //  by providing a temp allocator here - we'll never use
 | ||
|  |         //  this allocator because when our output is connected we'll
 | ||
|  |         //  reconnect this pin
 | ||
|  |         hr = CTransformInputPin::GetAllocator( ppAllocator ); | ||
|  |     } | ||
|  |     return hr; | ||
|  | 
 | ||
|  | } // GetAllocator
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /* Get told which allocator the upstream output pin is actually going to use */ | ||
|  | 
 | ||
|  | 
 | ||
|  | STDMETHODIMP | ||
|  | CTransInPlaceInputPin::NotifyAllocator( | ||
|  |     IMemAllocator * pAllocator, | ||
|  |     BOOL bReadOnly) | ||
|  | { | ||
|  |     HRESULT hr = S_OK; | ||
|  |     CheckPointer(pAllocator,E_POINTER); | ||
|  |     ValidateReadPtr(pAllocator,sizeof(IMemAllocator)); | ||
|  | 
 | ||
|  |     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
 | ||
|  |     } | ||
|  | 
 | ||
|  |     return hr; | ||
|  | 
 | ||
|  | } // NotifyAllocator
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // EnumMediaTypes
 | ||
|  | // - pass through to our downstream filter
 | ||
|  | STDMETHODIMP CTransInPlaceInputPin::EnumMediaTypes( __deref_out 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 ); | ||
|  | 
 | ||
|  | } // EnumMediaTypes
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // CheckMediaType
 | ||
|  | // - 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 CTransInPlaceInputPin::CheckMediaType(const CMediaType *pmt ) | ||
|  | { | ||
|  |     HRESULT hr = m_pTIPFilter->CheckInputType(pmt); | ||
|  |     if (hr!=S_OK) return hr; | ||
|  | 
 | ||
|  |     if( m_pTIPFilter->m_pOutput->IsConnected() ) | ||
|  |         return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt ); | ||
|  |     else | ||
|  |         return S_OK; | ||
|  | 
 | ||
|  | } // CheckMediaType
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // If upstream asks us what our requirements are, we will try to ask downstream
 | ||
|  | // if that doesn't work, we'll just take the defaults.
 | ||
|  | STDMETHODIMP | ||
|  | CTransInPlaceInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES *pProps) | ||
|  | { | ||
|  | 
 | ||
|  |     if( m_pTIPFilter->m_pOutput->IsConnected() ) | ||
|  |         return m_pTIPFilter->OutputPin() | ||
|  |                ->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps ); | ||
|  |     else | ||
|  |         return E_NOTIMPL; | ||
|  | 
 | ||
|  | } // GetAllocatorRequirements
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // CTransInPlaceInputPin::CompleteConnect() calls CBaseInputPin::CompleteConnect()
 | ||
|  | // and then calls CTransInPlaceFilter::CompleteConnect().  It does this because 
 | ||
|  | // CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not
 | ||
|  | // want to reconnect a pin if CBaseInputPin::CompleteConnect() fails.
 | ||
|  | HRESULT | ||
|  | CTransInPlaceInputPin::CompleteConnect(IPin *pReceivePin) | ||
|  | { | ||
|  |     HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin); | ||
|  |     if (FAILED(hr)) { | ||
|  |         return hr; | ||
|  |     } | ||
|  | 
 | ||
|  |     return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin); | ||
|  | } // CompleteConnect
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // =================================================================
 | ||
|  | // Implements the CTransInPlaceOutputPin class
 | ||
|  | // =================================================================
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // constructor
 | ||
|  | 
 | ||
|  | CTransInPlaceOutputPin::CTransInPlaceOutputPin( | ||
|  |     __in_opt LPCTSTR pObjectName, | ||
|  |     __inout CTransInPlaceFilter *pFilter, | ||
|  |     __inout HRESULT * phr, | ||
|  |     __in_opt LPCWSTR pPinName) | ||
|  |     : CTransformOutputPin( pObjectName | ||
|  |                          , pFilter | ||
|  |                          , phr | ||
|  |                          , pPinName), | ||
|  |       m_pTIPFilter(pFilter) | ||
|  | { | ||
|  |     DbgLog(( LOG_TRACE, 2 | ||
|  |            , TEXT("CTransInPlaceOutputPin::CTransInPlaceOutputPin"))); | ||
|  | 
 | ||
|  | } // constructor
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // EnumMediaTypes
 | ||
|  | // - pass through to our upstream filter
 | ||
|  | STDMETHODIMP CTransInPlaceOutputPin::EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum ) | ||
|  | { | ||
|  |     // Can only pass through if connected.
 | ||
|  |     if( ! m_pTIPFilter->m_pInput->IsConnected() ) | ||
|  |         return VFW_E_NOT_CONNECTED; | ||
|  | 
 | ||
|  |     return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum ); | ||
|  | 
 | ||
|  | } // EnumMediaTypes
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // CheckMediaType
 | ||
|  | // - agree to anything if not connected,
 | ||
|  | // otherwise pass through to the upstream filter.
 | ||
|  | 
 | ||
|  | HRESULT CTransInPlaceOutputPin::CheckMediaType(const CMediaType *pmt ) | ||
|  | { | ||
|  |     // Don't accept any output pin type changes if we're copying
 | ||
|  |     // between allocators - it's too late to change the input
 | ||
|  |     // allocator size.
 | ||
|  |     if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) { | ||
|  |         if (*pmt == m_mt) { | ||
|  |             return S_OK; | ||
|  |         } else { | ||
|  |             return VFW_E_TYPE_NOT_ACCEPTED; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // Assumes the type does not change.  That's why we're calling
 | ||
|  |     // CheckINPUTType here on the OUTPUT pin.
 | ||
|  |     HRESULT hr = m_pTIPFilter->CheckInputType(pmt); | ||
|  |     if (hr!=S_OK) return hr; | ||
|  | 
 | ||
|  |     if( m_pTIPFilter->m_pInput->IsConnected() ) | ||
|  |         return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt ); | ||
|  |     else | ||
|  |         return S_OK; | ||
|  | 
 | ||
|  | } // CheckMediaType
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /* Save the allocator pointer in the output pin
 | ||
|  | */ | ||
|  | void | ||
|  | CTransInPlaceOutputPin::SetAllocator(IMemAllocator * pAllocator) | ||
|  | { | ||
|  |     pAllocator->AddRef(); | ||
|  |     if (m_pAllocator) { | ||
|  |         m_pAllocator->Release(); | ||
|  |     } | ||
|  |     m_pAllocator = pAllocator; | ||
|  | } // SetAllocator
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // CTransInPlaceOutputPin::CompleteConnect() calls CBaseOutputPin::CompleteConnect()
 | ||
|  | // and then calls CTransInPlaceFilter::CompleteConnect().  It does this because 
 | ||
|  | // CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not want to 
 | ||
|  | // reconnect a pin if CBaseOutputPin::CompleteConnect() fails.  
 | ||
|  | // CBaseOutputPin::CompleteConnect() often fails when our output pin is being connected 
 | ||
|  | // to the Video Mixing Renderer.
 | ||
|  | HRESULT | ||
|  | CTransInPlaceOutputPin::CompleteConnect(IPin *pReceivePin) | ||
|  | { | ||
|  |     HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin); | ||
|  |     if (FAILED(hr)) { | ||
|  |         return hr; | ||
|  |     } | ||
|  | 
 | ||
|  |     return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin); | ||
|  | } // CompleteConnect
 |