topical media & game development

talk show tell print

hush-src-multi-BaseClasses-transip.cpp / cpp



  //------------------------------------------------------------------------------
  // File: TransIP.cpp
  //
  // Desc: DirectShow base classes - implements class for simple Transform-
  //       In-Place filters such as audio.
  //
  // Copyright (c) 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
     ( TCHAR     *pName,
       LPUNKNOWN  pUnk,
       REFCLSID   clsid,
       HRESULT   *phr,
       bool       bModifiesData
     )
     : CTransformFilter(pName, pUnk, clsid),
       m_bModifiesData(bModifiesData)
  {
  ifdef PERF
      RegisterPerfId();
  endif //  PERF
  
  } // constructor
  
  ifdef UNICODE
  CTransInPlaceFilter::CTransInPlaceFilter
     ( CHAR     *pName,
       LPUNKNOWN  pUnk,
       REFCLSID   clsid,
       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
              , 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 {
          // We're reduced to blind guessing.  Let's guess one byte and 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
  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 hr = pSample2->SetProperties(
              FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer),
              (PBYTE)m_pInput->SampleProps());
          pSample2->Release();
          if (FAILED(hr)) {
              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();
          pDest->SetActualDataLength(lDataLength);
  
          // Copy the sample data
          {
              BYTE *pSourceBuffer, *pDestBuffer;
              long lSourceSize  = pSource->GetSize();
              long lDestSize = pDest->GetSize();
  
              ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength);
  
              pSource->GetPointer(&pSourceBuffer);
              pDest->GetPointer(&pDestBuffer);
              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
      ( TCHAR               *pObjectName
      , CTransInPlaceFilter *pFilter
      , HRESULT             *phr
      , 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(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( 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(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(
      TCHAR *pObjectName,
      CTransInPlaceFilter *pFilter,
      HRESULT * phr,
      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( 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
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.