topical media & game development

talk show tell print

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



  //------------------------------------------------------------------------------
  // File: Transfrm.cpp
  //
  // Desc: DirectShow base classes - implements class for simple transform
  //       filters such as video decompressors.
  //
  // Copyright (c) Microsoft Corporation.  All rights reserved.
  //------------------------------------------------------------------------------
  
  include <streams.h>
  include <measure.h>
  
  // =================================================================
  // Implements the CTransformFilter class
  // =================================================================
  
  CTransformFilter::CTransformFilter(TCHAR     *pName,
                                     LPUNKNOWN pUnk,
                                     REFCLSID  clsid) :
      CBaseFilter(pName,pUnk,&m_csFilter, clsid),
      m_pInput(NULL),
      m_pOutput(NULL),
      m_bEOSDelivered(FALSE),
      m_bQualityChanged(FALSE),
      m_bSampleSkipped(FALSE)
  {
  ifdef PERF
      RegisterPerfId();
  endif //  PERF
  }
  
  ifdef UNICODE
  CTransformFilter::CTransformFilter(char     *pName,
                                     LPUNKNOWN pUnk,
                                     REFCLSID  clsid) :
      CBaseFilter(pName,pUnk,&m_csFilter, clsid),
      m_pInput(NULL),
      m_pOutput(NULL),
      m_bEOSDelivered(FALSE),
      m_bQualityChanged(FALSE),
      m_bSampleSkipped(FALSE)
  {
  ifdef PERF
      RegisterPerfId();
  endif //  PERF
  }
  endif
  
  // destructor
  
  CTransformFilter::~CTransformFilter()
  {
      // Delete the pins
  
      delete m_pInput;
      delete m_pOutput;
  }
  
  // Transform place holder - should never be called
  HRESULT CTransformFilter::Transform(IMediaSample * pIn, IMediaSample *pOut)
  {
      UNREFERENCED_PARAMETER(pIn);
      UNREFERENCED_PARAMETER(pOut);
      DbgBreak("CTransformFilter::Transform() should never be called");
      return E_UNEXPECTED;
  }
  
  // return the number of pins we provide
  
  int CTransformFilter::GetPinCount()
  {
      return 2;
  }
  
  // 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
  
  // We return the objects as and when they are needed. If either of these fails
  // then we return NULL, the assumption being that the caller will realise the
  // whole deal is off and destroy us - which in turn will delete everything.
  
  CBasePin *
  CTransformFilter::GetPin(int n)
  {
      HRESULT hr = S_OK;
  
      // Create an input pin if necessary
  
      if (m_pInput == NULL) {
  
          m_pInput = new CTransformInputPin(NAME("Transform input pin"),
                                            this,              // Owner filter
                                            &hr,               // Result code
                                            L"XForm In");      // Pin name
  
          //  Can't fail
          ASSERT(SUCCEEDED(hr));
          if (m_pInput == NULL) {
              return NULL;
          }
          m_pOutput = (CTransformOutputPin *)
                     new CTransformOutputPin(NAME("Transform output pin"),
                                              this,            // Owner filter
                                              &hr,             // Result code
                                              L"XForm Out");   // Pin name
  
          // Can't fail
          ASSERT(SUCCEEDED(hr));
          if (m_pOutput == NULL) {
              delete m_pInput;
              m_pInput = NULL;
          }
      }
  
      // Return the appropriate pin
  
      if (n == 0) {
          return m_pInput;
      } else
      if (n == 1) {
          return m_pOutput;
      } else {
          return NULL;
      }
  }
  
  //
  // FindPin
  //
  // If Id is In or Out then return the IPin* for that pin
  // creating the pin if need be.  Otherwise return NULL with an error.
  
  STDMETHODIMP CTransformFilter::FindPin(LPCWSTR Id, IPin **ppPin)
  {
      CheckPointer(ppPin,E_POINTER);
      ValidateReadWritePtr(ppPin,sizeof(IPin *));
  
      if (0==lstrcmpW(Id,L"In")) {
          *ppPin = GetPin(0);
      } else if (0==lstrcmpW(Id,L"Out")) {
          *ppPin = GetPin(1);
      } else {
          *ppPin = NULL;
          return VFW_E_NOT_FOUND;
      }
  
      HRESULT hr = NOERROR;
      //  AddRef() returned pointer - but GetPin could fail if memory is low.
      if (*ppPin) {
          (*ppPin)->AddRef();
      } else {
          hr = E_OUTOFMEMORY;  // probably.  There's no pin anyway.
      }
      return hr;
  }
  
  // override these two functions if you want to inform something
  // about entry to or exit from streaming state.
  
  HRESULT
  CTransformFilter::StartStreaming()
  {
      return NOERROR;
  }
  
  HRESULT
  CTransformFilter::StopStreaming()
  {
      return NOERROR;
  }
  
  // override this to grab extra interfaces on connection
  
  HRESULT
  CTransformFilter::CheckConnect(PIN_DIRECTION dir,IPin *pPin)
  {
      UNREFERENCED_PARAMETER(dir);
      UNREFERENCED_PARAMETER(pPin);
      return NOERROR;
  }
  
  // place holder to allow derived classes to release any extra interfaces
  
  HRESULT
  CTransformFilter::BreakConnect(PIN_DIRECTION dir)
  {
      UNREFERENCED_PARAMETER(dir);
      return NOERROR;
  }
  
  // Let derived classes know about connection completion
  
  HRESULT
  CTransformFilter::CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin)
  {
      UNREFERENCED_PARAMETER(direction);
      UNREFERENCED_PARAMETER(pReceivePin);
      return NOERROR;
  }
  
  // override this to know when the media type is really set
  
  HRESULT
  CTransformFilter::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt)
  {
      UNREFERENCED_PARAMETER(direction);
      UNREFERENCED_PARAMETER(pmt);
      return NOERROR;
  }
  
  // Set up our output sample
  HRESULT
  CTransformFilter::InitializeOutputSample(IMediaSample *pSample, IMediaSample **ppOutSample)
  {
      IMediaSample *pOutSample;
  
      // default - times are the same
  
      AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
      DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;
  
      // This will prevent the image renderer from switching us to DirectDraw
      // when we can't do it without skipping frames because we're not on a
      // keyframe.  If it really has to switch us, it still will, but then we
      // will have to wait for the next keyframe
      if (!(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) {
          dwFlags |= AM_GBF_NOTASYNCPOINT;
      }
  
      ASSERT(m_pOutput->m_pAllocator != NULL);
      HRESULT hr = m_pOutput->m_pAllocator->GetBuffer(
               &pOutSample
               , pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ?
                     &pProps->tStart : NULL
               , pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ?
                     &pProps->tStop : NULL
               , dwFlags
           );
      *ppOutSample = pOutSample;
      if (FAILED(hr)) {
          return hr;
      }
  
      ASSERT(pOutSample);
      IMediaSample2 *pOutSample2;
      if (SUCCEEDED(pOutSample->QueryInterface(IID_IMediaSample2,
                                               (void **)&pOutSample2))) {
          /*  Modify it */
          AM_SAMPLE2_PROPERTIES OutProps;
          EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties(
              FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps)
          ));
          OutProps.dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
          OutProps.dwSampleFlags =
              (OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) |
              (pProps->dwSampleFlags & ~AM_SAMPLE_TYPECHANGED);
          OutProps.tStart = pProps->tStart;
          OutProps.tStop  = pProps->tStop;
          OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId);
          hr = pOutSample2->SetProperties(
              FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId),
              (PBYTE)&OutProps
          );
          if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
              m_bSampleSkipped = FALSE;
          }
          pOutSample2->Release();
      } else {
          if (pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) {
              pOutSample->SetTime(&pProps->tStart,
                                  &pProps->tStop);
          }
          if (pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT) {
              pOutSample->SetSyncPoint(TRUE);
          }
          if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
              pOutSample->SetDiscontinuity(TRUE);
              m_bSampleSkipped = FALSE;
          }
          // Copy the media times
  
          LONGLONG MediaStart, MediaEnd;
          if (pSample->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) {
              pOutSample->SetMediaTime(&MediaStart,&MediaEnd);
          }
      }
      return S_OK;
  }
  
  // override this to customize the transform process
  
  HRESULT
  CTransformFilter::Receive(IMediaSample *pSample)
  {
      /*  Check for other streams and pass them on */
      AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
      if (pProps->dwStreamId != AM_STREAM_MEDIA) {
          return m_pOutput->m_pInputPin->Receive(pSample);
      }
      HRESULT hr;
      ASSERT(pSample);
      IMediaSample * pOutSample;
  
      // If no output to deliver to then no point sending us data
  
      ASSERT (m_pOutput != NULL) ;
  
      // Set up the output sample
      hr = InitializeOutputSample(pSample, &pOutSample);
  
      if (FAILED(hr)) {
          return hr;
      }
  
      // Start timing the transform (if PERF is defined)
      MSR_START(m_idTransform);
  
      // have the derived class transform the data
  
      hr = Transform(pSample, pOutSample);
  
      // Stop the clock and log it (if PERF is defined)
      MSR_STOP(m_idTransform);
  
      if (FAILED(hr)) {
          DbgLog((LOG_TRACE,1,TEXT("Error from transform")));
      } else {
          // the Transform() function can return S_FALSE to indicate that the
          // sample should not be delivered; we only deliver the sample if it's
          // really S_OK (same as NOERROR, of course.)
          if (hr == NOERROR) {
                  hr = m_pOutput->m_pInputPin->Receive(pOutSample);
              m_bSampleSkipped = FALSE;        // last thing no longer dropped
          } else {
              // S_FALSE returned from Transform is a PRIVATE agreement
              // We should return NOERROR from Receive() in this cause because returning S_FALSE
              // from Receive() means that this is the end of the stream and no more data should
              // be sent.
              if (S_FALSE == hr) {
  
                  //  Release the sample before calling notify to avoid
                  //  deadlocks if the sample holds a lock on the system
                  //  such as DirectDraw buffers do
                  pOutSample->Release();
                  m_bSampleSkipped = TRUE;
                  if (!m_bQualityChanged) {
                      NotifyEvent(EC_QUALITY_CHANGE,0,0);
                      m_bQualityChanged = TRUE;
                  }
                  return NOERROR;
              }
          }
      }
  
      // release the output buffer. If the connected pin still needs it,
      // it will have addrefed it itself.
      pOutSample->Release();
  
      return hr;
  }
  
  // Return S_FALSE to mean "pass the note on upstream"
  // Return NOERROR (Same as S_OK)
  // to mean "I've done something about it, don't pass it on"
  HRESULT CTransformFilter::AlterQuality(Quality q)
  {
      UNREFERENCED_PARAMETER(q);
      return S_FALSE;
  }
  
  // EndOfStream received. Default behaviour is to deliver straight
  // downstream, since we have no queued data. If you overrode Receive
  // and have queue data, then you need to handle this and deliver EOS after
  // all queued data is sent
  HRESULT
  CTransformFilter::EndOfStream(void)
  {
      HRESULT hr = NOERROR;
      if (m_pOutput != NULL) {
          hr = m_pOutput->DeliverEndOfStream();
      }
  
      return hr;
  }
  
  // enter flush state. Receives already blocked
  // must override this if you have queued data or a worker thread
  HRESULT
  CTransformFilter::BeginFlush(void)
  {
      HRESULT hr = NOERROR;
      if (m_pOutput != NULL) {
          // block receives -- done by caller (CBaseInputPin::BeginFlush)
  
          // discard queued data -- we have no queued data
  
          // free anyone blocked on receive - not possible in this filter
  
          // call downstream
          hr = m_pOutput->DeliverBeginFlush();
      }
      return hr;
  }
  
  // leave flush state. must override this if you have queued data
  // or a worker thread
  HRESULT
  CTransformFilter::EndFlush(void)
  {
      // sync with pushing thread -- we have no worker thread
  
      // ensure no more data to go downstream -- we have no queued data
  
      // call EndFlush on downstream pins
      ASSERT (m_pOutput != NULL);
      return m_pOutput->DeliverEndFlush();
  
      // caller (the input pin's method) will unblock Receives
  }
  
  // override these so that the derived filter can catch them
  
  STDMETHODIMP
  CTransformFilter::Stop()
  {
      CAutoLock lck1(&m_csFilter);
      if (m_State == State_Stopped) {
          return NOERROR;
      }
  
      // Succeed the Stop if we are not completely connected
  
      ASSERT(m_pInput == NULL || m_pOutput != NULL);
      if (m_pInput == NULL || m_pInput->IsConnected() == FALSE ||
          m_pOutput->IsConnected() == FALSE) {
                  m_State = State_Stopped;
                  m_bEOSDelivered = FALSE;
                  return NOERROR;
      }
  
      ASSERT(m_pInput);
      ASSERT(m_pOutput);
  
      // decommit the input pin before locking or we can deadlock
      m_pInput->Inactive();
  
      // synchronize with Receive calls
  
      CAutoLock lck2(&m_csReceive);
      m_pOutput->Inactive();
  
      // allow a class derived from CTransformFilter
      // to know about starting and stopping streaming
  
      HRESULT hr = StopStreaming();
      if (SUCCEEDED(hr)) {
          // complete the state transition
          m_State = State_Stopped;
          m_bEOSDelivered = FALSE;
      }
      return hr;
  }
  
  STDMETHODIMP
  CTransformFilter::Pause()
  {
      CAutoLock lck(&m_csFilter);
      HRESULT hr = NOERROR;
  
      if (m_State == State_Paused) {
          // (This space left deliberately blank)
      }
  
      // If we have no input pin or it isn't yet connected then when we are
      // asked to pause we deliver an end of stream to the downstream filter.
      // This makes sure that it doesn't sit there forever waiting for
      // samples which we cannot ever deliver without an input connection.
  
      else if (m_pInput == NULL || m_pInput->IsConnected() == FALSE) {
          if (m_pOutput && m_bEOSDelivered == FALSE) {
              m_pOutput->DeliverEndOfStream();
              m_bEOSDelivered = TRUE;
          }
          m_State = State_Paused;
      }
  
      // We may have an input connection but no output connection
      // However, if we have an input pin we do have an output pin
  
      else if (m_pOutput->IsConnected() == FALSE) {
          m_State = State_Paused;
      }
  
      else {
          if (m_State == State_Stopped) {
              // allow a class derived from CTransformFilter
              // to know about starting and stopping streaming
              CAutoLock lck2(&m_csReceive);
              hr = StartStreaming();
          }
          if (SUCCEEDED(hr)) {
              hr = CBaseFilter::Pause();
          }
      }
  
      m_bSampleSkipped = FALSE;
      m_bQualityChanged = FALSE;
      return hr;
  }
  
  HRESULT
  CTransformFilter::NewSegment(
      REFERENCE_TIME tStart,
      REFERENCE_TIME tStop,
      double dRate)
  {
      if (m_pOutput != NULL) {
          return m_pOutput->DeliverNewSegment(tStart, tStop, dRate);
      }
      return S_OK;
  }
  
  // Check streaming status
  HRESULT
  CTransformInputPin::CheckStreaming()
  {
      ASSERT(m_pTransformFilter->m_pOutput != NULL);
      if (!m_pTransformFilter->m_pOutput->IsConnected()) {
          return VFW_E_NOT_CONNECTED;
      } else {
          //  Shouldn't be able to get any data if we're not connected!
          ASSERT(IsConnected());
  
          //  we're flushing
          if (m_bFlushing) {
              return S_FALSE;
          }
          //  Don't process stuff in Stopped state
          if (IsStopped()) {
              return VFW_E_WRONG_STATE;
          }
          if (m_bRunTimeError) {
                  return VFW_E_RUNTIME_ERROR;
          }
          return S_OK;
      }
  }
  
  // =================================================================
  // Implements the CTransformInputPin class
  // =================================================================
  
  // constructor
  
  CTransformInputPin::CTransformInputPin(
      TCHAR *pObjectName,
      CTransformFilter *pTransformFilter,
      HRESULT * phr,
      LPCWSTR pName)
      : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName)
  {
      DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));
      m_pTransformFilter = pTransformFilter;
  }
  
  ifdef UNICODE
  CTransformInputPin::CTransformInputPin(
      CHAR *pObjectName,
      CTransformFilter *pTransformFilter,
      HRESULT * phr,
      LPCWSTR pName)
      : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName)
  {
      DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));
      m_pTransformFilter = pTransformFilter;
  }
  endif
  
  // provides derived filter a chance to grab extra interfaces
  
  HRESULT
  CTransformInputPin::CheckConnect(IPin *pPin)
  {
      HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_INPUT,pPin);
      if (FAILED(hr)) {
              return hr;
      }
      return CBaseInputPin::CheckConnect(pPin);
  }
  
  // provides derived filter a chance to release it's extra interfaces
  
  HRESULT
  CTransformInputPin::BreakConnect()
  {
      //  Can't disconnect unless stopped
      ASSERT(IsStopped());
      m_pTransformFilter->BreakConnect(PINDIR_INPUT);
      return CBaseInputPin::BreakConnect();
  }
  
  // Let derived class know when the input pin is connected
  
  HRESULT
  CTransformInputPin::CompleteConnect(IPin *pReceivePin)
  {
      HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
      if (FAILED(hr)) {
          return hr;
      }
      return CBaseInputPin::CompleteConnect(pReceivePin);
  }
  
  // check that we can support a given media type
  
  HRESULT
  CTransformInputPin::CheckMediaType(const CMediaType* pmt)
  {
      // Check the input type
  
      HRESULT hr = m_pTransformFilter->CheckInputType(pmt);
      if (S_OK != hr) {
          return hr;
      }
  
      // if the output pin is still connected, then we have
      // to check the transform not just the input format
  
      if ((m_pTransformFilter->m_pOutput != NULL) &&
          (m_pTransformFilter->m_pOutput->IsConnected())) {
              return m_pTransformFilter->CheckTransform(
                        pmt,
                        &m_pTransformFilter->m_pOutput->CurrentMediaType());
      } else {
          return hr;
      }
  }
  
  // set the media type for this connection
  
  HRESULT
  CTransformInputPin::SetMediaType(const CMediaType* mtIn)
  {
      // Set the base class media type (should always succeed)
      HRESULT hr = CBasePin::SetMediaType(mtIn);
      if (FAILED(hr)) {
          return hr;
      }
  
      // check the transform can be done (should always succeed)
      ASSERT(SUCCEEDED(m_pTransformFilter->CheckInputType(mtIn)));
  
      return m_pTransformFilter->SetMediaType(PINDIR_INPUT,mtIn);
  }
  
  // =================================================================
  // Implements IMemInputPin interface
  // =================================================================
  
  // provide EndOfStream that passes straight downstream
  // (there is no queued data)
  STDMETHODIMP
  CTransformInputPin::EndOfStream(void)
  {
      CAutoLock lck(&m_pTransformFilter->m_csReceive);
      HRESULT hr = CheckStreaming();
      if (S_OK == hr) {
         hr = m_pTransformFilter->EndOfStream();
      }
      return hr;
  }
  
  // enter flushing state. Call default handler to block Receives, then
  // pass to overridable method in filter
  STDMETHODIMP
  CTransformInputPin::BeginFlush(void)
  {
      CAutoLock lck(&m_pTransformFilter->m_csFilter);
      //  Are we actually doing anything?
      ASSERT(m_pTransformFilter->m_pOutput != NULL);
      if (!IsConnected() ||
          !m_pTransformFilter->m_pOutput->IsConnected()) {
          return VFW_E_NOT_CONNECTED;
      }
      HRESULT hr = CBaseInputPin::BeginFlush();
      if (FAILED(hr)) {
              return hr;
      }
  
      return m_pTransformFilter->BeginFlush();
  }
  
  // leave flushing state.
  // Pass to overridable method in filter, then call base class
  // to unblock receives (finally)
  STDMETHODIMP
  CTransformInputPin::EndFlush(void)
  {
      CAutoLock lck(&m_pTransformFilter->m_csFilter);
      //  Are we actually doing anything?
      ASSERT(m_pTransformFilter->m_pOutput != NULL);
      if (!IsConnected() ||
          !m_pTransformFilter->m_pOutput->IsConnected()) {
          return VFW_E_NOT_CONNECTED;
      }
  
      HRESULT hr = m_pTransformFilter->EndFlush();
      if (FAILED(hr)) {
          return hr;
      }
  
      return CBaseInputPin::EndFlush();
  }
  
  // here's the next block of data from the stream.
  // AddRef it yourself if you need to hold it beyond the end
  // of this call.
  
  HRESULT
  CTransformInputPin::Receive(IMediaSample * pSample)
  {
      HRESULT hr;
      CAutoLock lck(&m_pTransformFilter->m_csReceive);
      ASSERT(pSample);
  
      // check all is well with the base class
      hr = CBaseInputPin::Receive(pSample);
      if (S_OK == hr) {
          hr = m_pTransformFilter->Receive(pSample);
      }
      return hr;
  }
  
  // override to pass downstream
  STDMETHODIMP
  CTransformInputPin::NewSegment(
      REFERENCE_TIME tStart,
      REFERENCE_TIME tStop,
      double dRate)
  {
      //  Save the values in the pin
      CBasePin::NewSegment(tStart, tStop, dRate);
      return m_pTransformFilter->NewSegment(tStart, tStop, dRate);
  }
  
  // =================================================================
  // Implements the CTransformOutputPin class
  // =================================================================
  
  // constructor
  
  CTransformOutputPin::CTransformOutputPin(
      TCHAR *pObjectName,
      CTransformFilter *pTransformFilter,
      HRESULT * phr,
      LPCWSTR pPinName)
      : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),
        m_pPosition(NULL)
  {
      DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));
      m_pTransformFilter = pTransformFilter;
  
  }
  
  ifdef UNICODE
  CTransformOutputPin::CTransformOutputPin(
      CHAR *pObjectName,
      CTransformFilter *pTransformFilter,
      HRESULT * phr,
      LPCWSTR pPinName)
      : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),
        m_pPosition(NULL)
  {
      DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));
      m_pTransformFilter = pTransformFilter;
  
  }
  endif
  
  // destructor
  
  CTransformOutputPin::~CTransformOutputPin()
  {
      DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::~CTransformOutputPin")));
  
      if (m_pPosition) m_pPosition->Release();
  }
  
  // overriden to expose IMediaPosition and IMediaSeeking control interfaces
  
  STDMETHODIMP
  CTransformOutputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv)
  {
      CheckPointer(ppv,E_POINTER);
      ValidateReadWritePtr(ppv,sizeof(PVOID));
      *ppv = NULL;
  
      if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
  
          // we should have an input pin by now
  
          ASSERT(m_pTransformFilter->m_pInput != NULL);
  
          if (m_pPosition == NULL) {
  
              HRESULT hr = CreatePosPassThru(
                               GetOwner(),
                               FALSE,
                               (IPin *)m_pTransformFilter->m_pInput,
                               &m_pPosition);
              if (FAILED(hr)) {
                  return hr;
              }
          }
          return m_pPosition->QueryInterface(riid, ppv);
      } else {
          return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
      }
  }
  
  // provides derived filter a chance to grab extra interfaces
  
  HRESULT
  CTransformOutputPin::CheckConnect(IPin *pPin)
  {
      // we should have an input connection first
  
      ASSERT(m_pTransformFilter->m_pInput != NULL);
      if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {
              return E_UNEXPECTED;
      }
  
      HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_OUTPUT,pPin);
      if (FAILED(hr)) {
              return hr;
      }
      return CBaseOutputPin::CheckConnect(pPin);
  }
  
  // provides derived filter a chance to release it's extra interfaces
  
  HRESULT
  CTransformOutputPin::BreakConnect()
  {
      //  Can't disconnect unless stopped
      ASSERT(IsStopped());
      m_pTransformFilter->BreakConnect(PINDIR_OUTPUT);
      return CBaseOutputPin::BreakConnect();
  }
  
  // Let derived class know when the output pin is connected
  
  HRESULT
  CTransformOutputPin::CompleteConnect(IPin *pReceivePin)
  {
      HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
      if (FAILED(hr)) {
          return hr;
      }
      return CBaseOutputPin::CompleteConnect(pReceivePin);
  }
  
  // check a given transform - must have selected input type first
  
  HRESULT
  CTransformOutputPin::CheckMediaType(const CMediaType* pmtOut)
  {
      // must have selected input first
      ASSERT(m_pTransformFilter->m_pInput != NULL);
      if ((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {
                  return E_INVALIDARG;
      }
  
      return m_pTransformFilter->CheckTransform(
                                      &m_pTransformFilter->m_pInput->CurrentMediaType(),
                                      pmtOut);
  }
  
  // called after we have agreed a media type to actually set it in which case
  // we run the CheckTransform function to get the output format type again
  
  HRESULT
  CTransformOutputPin::SetMediaType(const CMediaType* pmtOut)
  {
      HRESULT hr = NOERROR;
      ASSERT(m_pTransformFilter->m_pInput != NULL);
  
      ASSERT(m_pTransformFilter->m_pInput->CurrentMediaType().IsValid());
  
      // Set the base class media type (should always succeed)
      hr = CBasePin::SetMediaType(pmtOut);
      if (FAILED(hr)) {
          return hr;
      }
  
  ifdef DEBUG
      if (FAILED(m_pTransformFilter->CheckTransform(&m_pTransformFilter->
                                          m_pInput->CurrentMediaType(),pmtOut))) {
          DbgLog((LOG_ERROR,0,TEXT("*** This filter is accepting an output media type")));
          DbgLog((LOG_ERROR,0,TEXT("    that it can't currently transform to.  I hope")));
          DbgLog((LOG_ERROR,0,TEXT("    it's smart enough to reconnect its input.")));
      }
  endif
  
      return m_pTransformFilter->SetMediaType(PINDIR_OUTPUT,pmtOut);
  }
  
  // pass the buffer size decision through to the main transform class
  
  HRESULT
  CTransformOutputPin::DecideBufferSize(
      IMemAllocator * pAllocator,
      ALLOCATOR_PROPERTIES* pProp)
  {
      return m_pTransformFilter->DecideBufferSize(pAllocator, pProp);
  }
  
  // return a specific media type indexed by iPosition
  
  HRESULT
  CTransformOutputPin::GetMediaType(
      int iPosition,
      CMediaType *pMediaType)
  {
      ASSERT(m_pTransformFilter->m_pInput != NULL);
  
      //  We don't have any media types if our input is not connected
  
      if (m_pTransformFilter->m_pInput->IsConnected()) {
          return m_pTransformFilter->GetMediaType(iPosition,pMediaType);
      } else {
          return VFW_S_NO_MORE_ITEMS;
      }
  }
  
  // Override this if you can do something constructive to act on the
  // quality message.  Consider passing it upstream as well
  
  // Pass the quality mesage on upstream.
  
  STDMETHODIMP
  CTransformOutputPin::Notify(IBaseFilter * pSender, Quality q)
  {
      UNREFERENCED_PARAMETER(pSender);
      ValidateReadPtr(pSender,sizeof(IBaseFilter));
  
      // First see if we want to handle this ourselves
      HRESULT hr = m_pTransformFilter->AlterQuality(q);
      if (hr!=S_FALSE) {
          return hr;        // either S_OK or a failure
      }
  
      // S_FALSE means we pass the message on.
      // Find the quality sink for our input pin and send it there
  
      ASSERT(m_pTransformFilter->m_pInput != NULL);
  
      return m_pTransformFilter->m_pInput->PassNotify(q);
  
  } // Notify
  
  // the following removes a very large number of level 4 warnings from the microsoft
  // compiler output, which are not useful at all in this case.
  #pragma warning(disable:4514)
  


(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.