home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / BaseClasses / transfrm.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  27.4 KB  |  975 lines

  1. //------------------------------------------------------------------------------
  2. // File: Transfrm.cpp
  3. //
  4. // Desc: DirectShow base classes - implements class for simple transform
  5. //       filters such as video decompressors.
  6. //
  7. // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10.  
  11. #include <streams.h>
  12. #include <measure.h>
  13.  
  14.  
  15. // =================================================================
  16. // Implements the CTransformFilter class
  17. // =================================================================
  18.  
  19. CTransformFilter::CTransformFilter(TCHAR     *pName,
  20.                                    LPUNKNOWN pUnk,
  21.                                    REFCLSID  clsid) :
  22.     CBaseFilter(pName,pUnk,&m_csFilter, clsid),
  23.     m_pInput(NULL),
  24.     m_pOutput(NULL),
  25.     m_bEOSDelivered(FALSE),
  26.     m_bQualityChanged(FALSE),
  27.     m_bSampleSkipped(FALSE) {
  28. #ifdef PERF
  29.     RegisterPerfId();
  30. #endif //  PERF
  31. }
  32.  
  33. #ifdef UNICODE
  34. CTransformFilter::CTransformFilter(char     *pName,
  35.                                    LPUNKNOWN pUnk,
  36.                                    REFCLSID  clsid) :
  37.     CBaseFilter(pName,pUnk,&m_csFilter, clsid),
  38.     m_pInput(NULL),
  39.     m_pOutput(NULL),
  40.     m_bEOSDelivered(FALSE),
  41.     m_bQualityChanged(FALSE),
  42.     m_bSampleSkipped(FALSE) {
  43. #ifdef PERF
  44.     RegisterPerfId();
  45. #endif //  PERF
  46. }
  47. #endif
  48.  
  49. // destructor
  50.  
  51. CTransformFilter::~CTransformFilter() {
  52.     // Delete the pins
  53.  
  54.     delete m_pInput;
  55.     delete m_pOutput;
  56. }
  57.  
  58.  
  59. // Transform place holder - should never be called
  60. HRESULT CTransformFilter::Transform(IMediaSample * pIn, IMediaSample *pOut) {
  61.     UNREFERENCED_PARAMETER(pIn);
  62.     UNREFERENCED_PARAMETER(pOut);
  63.     DbgBreak("CTransformFilter::Transform() should never be called");
  64.     return E_UNEXPECTED;
  65. }
  66.  
  67.  
  68. // return the number of pins we provide
  69.  
  70. int CTransformFilter::GetPinCount() {
  71.     return 2;
  72. }
  73.  
  74.  
  75. // return a non-addrefed CBasePin * for the user to addref if he holds onto it
  76. // for longer than his pointer to us. We create the pins dynamically when they
  77. // are asked for rather than in the constructor. This is because we want to
  78. // give the derived class an oppportunity to return different pin objects
  79.  
  80. // We return the objects as and when they are needed. If either of these fails
  81. // then we return NULL, the assumption being that the caller will realise the
  82. // whole deal is off and destroy us - which in turn will delete everything.
  83.  
  84. CBasePin *
  85. CTransformFilter::GetPin(int n) {
  86.     HRESULT hr = S_OK;
  87.  
  88.     // Create an input pin if necessary
  89.  
  90.     if(m_pInput == NULL) {
  91.  
  92.         m_pInput = new CTransformInputPin(NAME("Transform input pin"),
  93.             this,              // Owner filter
  94.             &hr,               // Result code
  95.             L"XForm In");      // Pin name
  96.  
  97.  
  98.         //  Can't fail
  99.         ASSERT(SUCCEEDED(hr));
  100.         if(m_pInput == NULL) {
  101.             return NULL;
  102.         }
  103.         m_pOutput = (CTransformOutputPin *)
  104.             new CTransformOutputPin(NAME("Transform output pin"),
  105.             this,            // Owner filter
  106.             &hr,             // Result code
  107.             L"XForm Out");   // Pin name
  108.  
  109.  
  110.         // Can't fail
  111.         ASSERT(SUCCEEDED(hr));
  112.         if(m_pOutput == NULL) {
  113.             delete m_pInput;
  114.             m_pInput = NULL;
  115.         }
  116.     }
  117.  
  118.     // Return the appropriate pin
  119.  
  120.     if(n == 0) {
  121.         return m_pInput;
  122.     }
  123.     else
  124.         if(n == 1) {
  125.         return m_pOutput;
  126.     }
  127.     else {
  128.         return NULL;
  129.     }
  130. }
  131.  
  132.  
  133. //
  134. // FindPin
  135. //
  136. // If Id is In or Out then return the IPin* for that pin
  137. // creating the pin if need be.  Otherwise return NULL with an error.
  138.  
  139. STDMETHODIMP CTransformFilter::FindPin(LPCWSTR Id, IPin **ppPin) {
  140.     CheckPointer(ppPin,E_POINTER);
  141.     ValidateReadWritePtr(ppPin,sizeof(IPin *));
  142.  
  143.     if(0==lstrcmpW(Id,L"In")) {
  144.         *ppPin = GetPin(0);
  145.     }
  146.     else if(0==lstrcmpW(Id,L"Out")) {
  147.         *ppPin = GetPin(1);
  148.     }
  149.     else {
  150.         *ppPin = NULL;
  151.         return VFW_E_NOT_FOUND;
  152.     }
  153.  
  154.     HRESULT hr = NOERROR;
  155.     //  AddRef() returned pointer - but GetPin could fail if memory is low.
  156.     if(*ppPin) {
  157.         (*ppPin)->AddRef();
  158.     }
  159.     else {
  160.         hr = E_OUTOFMEMORY;  // probably.  There's no pin anyway.
  161.     }
  162.     return hr;
  163. }
  164.  
  165.  
  166. // override these two functions if you want to inform something
  167. // about entry to or exit from streaming state.
  168.  
  169. HRESULT
  170. CTransformFilter::StartStreaming() {
  171.     return NOERROR;
  172. }
  173.  
  174.  
  175. HRESULT
  176. CTransformFilter::StopStreaming() {
  177.     return NOERROR;
  178. }
  179.  
  180.  
  181. // override this to grab extra interfaces on connection
  182.  
  183. HRESULT
  184. CTransformFilter::CheckConnect(PIN_DIRECTION dir,IPin *pPin) {
  185.     UNREFERENCED_PARAMETER(dir);
  186.     UNREFERENCED_PARAMETER(pPin);
  187.     return NOERROR;
  188. }
  189.  
  190.  
  191. // place holder to allow derived classes to release any extra interfaces
  192.  
  193. HRESULT
  194. CTransformFilter::BreakConnect(PIN_DIRECTION dir) {
  195.     UNREFERENCED_PARAMETER(dir);
  196.     return NOERROR;
  197. }
  198.  
  199.  
  200. // Let derived classes know about connection completion
  201.  
  202. HRESULT
  203. CTransformFilter::CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin) {
  204.     UNREFERENCED_PARAMETER(direction);
  205.     UNREFERENCED_PARAMETER(pReceivePin);
  206.     return NOERROR;
  207. }
  208.  
  209.  
  210. // override this to know when the media type is really set
  211.  
  212. HRESULT
  213. CTransformFilter::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt) {
  214.     UNREFERENCED_PARAMETER(direction);
  215.     UNREFERENCED_PARAMETER(pmt);
  216.     return NOERROR;
  217. }
  218.  
  219.  
  220. // Set up our output sample
  221. HRESULT
  222. CTransformFilter::InitializeOutputSample(IMediaSample *pSample, IMediaSample **ppOutSample) {
  223.     IMediaSample *pOutSample;
  224.  
  225.     // default - times are the same
  226.  
  227.     AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
  228.     DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0;
  229.  
  230.     // This will prevent the image renderer from switching us to DirectDraw
  231.     // when we can't do it without skipping frames because we're not on a
  232.     // keyframe.  If it really has to switch us, it still will, but then we
  233.     // will have to wait for the next keyframe
  234.     if(!(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) {
  235.         dwFlags |= AM_GBF_NOTASYNCPOINT;
  236.     }
  237.  
  238.     ASSERT(m_pOutput->m_pAllocator != NULL);
  239.     HRESULT hr = m_pOutput->m_pAllocator->GetBuffer(&pOutSample
  240.         , pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ?
  241.         &pProps->tStart : NULL
  242.         , pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ?
  243.         &pProps->tStop : NULL
  244.         , dwFlags);
  245.     *ppOutSample = pOutSample;
  246.     if(FAILED(hr)) {
  247.         return hr;
  248.     }
  249.  
  250.     ASSERT(pOutSample);
  251.     IMediaSample2 *pOutSample2;
  252.     if(SUCCEEDED(pOutSample->QueryInterface(IID_IMediaSample2,
  253.         (void **)&pOutSample2))) {
  254.         /*  Modify it */
  255.         AM_SAMPLE2_PROPERTIES OutProps;
  256.         EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps)));
  257.         OutProps.dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
  258.         OutProps.dwSampleFlags =
  259.             (OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) |
  260.             (pProps->dwSampleFlags & ~AM_SAMPLE_TYPECHANGED);
  261.  
  262.         OutProps.tStart = pProps->tStart;
  263.         OutProps.tStop  = pProps->tStop;
  264.         OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId);
  265.  
  266.         hr = pOutSample2->SetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId),
  267.             (PBYTE)&OutProps);
  268.         if(pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
  269.             m_bSampleSkipped = FALSE;
  270.         }
  271.         pOutSample2->Release();
  272.     }
  273.     else {
  274.         if(pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) {
  275.             pOutSample->SetTime(&pProps->tStart,
  276.                 &pProps->tStop);
  277.         }
  278.         if(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT) {
  279.             pOutSample->SetSyncPoint(TRUE);
  280.         }
  281.         if(pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) {
  282.             pOutSample->SetDiscontinuity(TRUE);
  283.             m_bSampleSkipped = FALSE;
  284.         }
  285.         // Copy the media times
  286.  
  287.         LONGLONG MediaStart, MediaEnd;
  288.         if(pSample->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) {
  289.             pOutSample->SetMediaTime(&MediaStart,&MediaEnd);
  290.         }
  291.     }
  292.     return S_OK;
  293. }
  294.  
  295. // override this to customize the transform process
  296.  
  297. HRESULT
  298. CTransformFilter::Receive(IMediaSample *pSample) {
  299.     /*  Check for other streams and pass them on */
  300.     AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps();
  301.     if(pProps->dwStreamId != AM_STREAM_MEDIA) {
  302.         return m_pOutput->m_pInputPin->Receive(pSample);
  303.     }
  304.     HRESULT hr;
  305.     ASSERT(pSample);
  306.     IMediaSample * pOutSample;
  307.  
  308.     // If no output to deliver to then no point sending us data
  309.  
  310.     ASSERT(m_pOutput != NULL) ;
  311.  
  312.     // Set up the output sample
  313.     hr = InitializeOutputSample(pSample, &pOutSample);
  314.  
  315.     if(FAILED(hr)) {
  316.         return hr;
  317.     }
  318.  
  319.     // Start timing the transform (if PERF is defined)
  320.     MSR_START(m_idTransform);
  321.  
  322.     // have the derived class transform the data
  323.  
  324.     hr = Transform(pSample, pOutSample);
  325.  
  326.     // Stop the clock and log it (if PERF is defined)
  327.     MSR_STOP(m_idTransform);
  328.  
  329.     if(FAILED(hr)) {
  330.         DbgLog((LOG_TRACE,1,TEXT("Error from transform")));
  331.     }
  332.     else {
  333.         // the Transform() function can return S_FALSE to indicate that the
  334.         // sample should not be delivered; we only deliver the sample if it's
  335.         // really S_OK (same as NOERROR, of course.)
  336.         if(hr == NOERROR) {
  337.             hr = m_pOutput->m_pInputPin->Receive(pOutSample);
  338.             m_bSampleSkipped = FALSE;   // last thing no longer dropped
  339.         }
  340.         else {
  341.             // S_FALSE returned from Transform is a PRIVATE agreement
  342.             // We should return NOERROR from Receive() in this cause because returning S_FALSE
  343.             // from Receive() means that this is the end of the stream and no more data should
  344.             // be sent.
  345.             if(S_FALSE == hr) {
  346.  
  347.                 //  Release the sample before calling notify to avoid
  348.                 //  deadlocks if the sample holds a lock on the system
  349.                 //  such as DirectDraw buffers do
  350.                 pOutSample->Release();
  351.                 m_bSampleSkipped = TRUE;
  352.                 if(!m_bQualityChanged) {
  353.                     NotifyEvent(EC_QUALITY_CHANGE,0,0);
  354.                     m_bQualityChanged = TRUE;
  355.                 }
  356.                 return NOERROR;
  357.             }
  358.         }
  359.     }
  360.  
  361.     // release the output buffer. If the connected pin still needs it,
  362.     // it will have addrefed it itself.
  363.     pOutSample->Release();
  364.  
  365.     return hr;
  366. }
  367.  
  368.  
  369. // Return S_FALSE to mean "pass the note on upstream"
  370. // Return NOERROR (Same as S_OK)
  371. // to mean "I've done something about it, don't pass it on"
  372. HRESULT CTransformFilter::AlterQuality(Quality q) {
  373.     UNREFERENCED_PARAMETER(q);
  374.     return S_FALSE;
  375. }
  376.  
  377.  
  378. // EndOfStream received. Default behaviour is to deliver straight
  379. // downstream, since we have no queued data. If you overrode Receive
  380. // and have queue data, then you need to handle this and deliver EOS after
  381. // all queued data is sent
  382. HRESULT
  383. CTransformFilter::EndOfStream(void) {
  384.     HRESULT hr = NOERROR;
  385.     if(m_pOutput != NULL) {
  386.         hr = m_pOutput->DeliverEndOfStream();
  387.     }
  388.  
  389.     return hr;
  390. }
  391.  
  392.  
  393. // enter flush state. Receives already blocked
  394. // must override this if you have queued data or a worker thread
  395. HRESULT
  396. CTransformFilter::BeginFlush(void) {
  397.     HRESULT hr = NOERROR;
  398.     if(m_pOutput != NULL) {
  399.         // block receives -- done by caller (CBaseInputPin::BeginFlush)
  400.  
  401.         // discard queued data -- we have no queued data
  402.  
  403.         // free anyone blocked on receive - not possible in this filter
  404.  
  405.         // call downstream
  406.         hr = m_pOutput->DeliverBeginFlush();
  407.     }
  408.     return hr;
  409. }
  410.  
  411.  
  412. // leave flush state. must override this if you have queued data
  413. // or a worker thread
  414. HRESULT
  415. CTransformFilter::EndFlush(void) {
  416.     // sync with pushing thread -- we have no worker thread
  417.  
  418.     // ensure no more data to go downstream -- we have no queued data
  419.  
  420.     // call EndFlush on downstream pins
  421.     ASSERT(m_pOutput != NULL);
  422.     return m_pOutput->DeliverEndFlush();
  423.  
  424.     // caller (the input pin's method) will unblock Receives
  425. }
  426.  
  427.  
  428. // override these so that the derived filter can catch them
  429.  
  430. STDMETHODIMP
  431. CTransformFilter::Stop() {
  432.     CAutoLock lck1(&m_csFilter);
  433.     if(m_State == State_Stopped) {
  434.         return NOERROR;
  435.     }
  436.  
  437.     // Succeed the Stop if we are not completely connected
  438.  
  439.     ASSERT(m_pInput == NULL || m_pOutput != NULL);
  440.     if(m_pInput == NULL || m_pInput->IsConnected() == FALSE ||
  441.         m_pOutput->IsConnected() == FALSE) {
  442.         m_State = State_Stopped;
  443.         m_bEOSDelivered = FALSE;
  444.         return NOERROR;
  445.     }
  446.  
  447.     ASSERT(m_pInput);
  448.     ASSERT(m_pOutput);
  449.  
  450.     // decommit the input pin before locking or we can deadlock
  451.     m_pInput->Inactive();
  452.  
  453.     // synchronize with Receive calls
  454.  
  455.     CAutoLock lck2(&m_csReceive);
  456.     m_pOutput->Inactive();
  457.  
  458.     // allow a class derived from CTransformFilter
  459.     // to know about starting and stopping streaming
  460.  
  461.     HRESULT hr = StopStreaming();
  462.     if(SUCCEEDED(hr)) {
  463.         // complete the state transition
  464.         m_State = State_Stopped;
  465.         m_bEOSDelivered = FALSE;
  466.     }
  467.     return hr;
  468. }
  469.  
  470.  
  471. STDMETHODIMP
  472. CTransformFilter::Pause() {
  473.     CAutoLock lck(&m_csFilter);
  474.     HRESULT hr = NOERROR;
  475.  
  476.     if(m_State == State_Paused) {
  477.         // (This space left deliberately blank)
  478.     }
  479.  
  480.     // If we have no input pin or it isn't yet connected then when we are
  481.     // asked to pause we deliver an end of stream to the downstream filter.
  482.     // This makes sure that it doesn't sit there forever waiting for
  483.     // samples which we cannot ever deliver without an input connection.
  484.  
  485.     else if(m_pInput == NULL || m_pInput->IsConnected() == FALSE) {
  486.         if(m_pOutput && m_bEOSDelivered == FALSE) {
  487.             m_pOutput->DeliverEndOfStream();
  488.             m_bEOSDelivered = TRUE;
  489.         }
  490.         m_State = State_Paused;
  491.     }
  492.  
  493.     // We may have an input connection but no output connection
  494.     // However, if we have an input pin we do have an output pin
  495.  
  496.     else if(m_pOutput->IsConnected() == FALSE) {
  497.         m_State = State_Paused;
  498.     }
  499.     else {
  500.         if(m_State == State_Stopped) {
  501.             // allow a class derived from CTransformFilter
  502.             // to know about starting and stopping streaming
  503.             CAutoLock lck2(&m_csReceive);
  504.             hr = StartStreaming();
  505.         }
  506.         if(SUCCEEDED(hr)) {
  507.             hr = CBaseFilter::Pause();
  508.         }
  509.     }
  510.  
  511.     m_bSampleSkipped = FALSE;
  512.     m_bQualityChanged = FALSE;
  513.     return hr;
  514. }
  515.  
  516. HRESULT
  517. CTransformFilter::NewSegment(
  518.     REFERENCE_TIME tStart,
  519.     REFERENCE_TIME tStop,
  520.     double dRate) {
  521.     if(m_pOutput != NULL) {
  522.         return m_pOutput->DeliverNewSegment(tStart, tStop, dRate);
  523.     }
  524.     return S_OK;
  525. }
  526.  
  527. // Check streaming status
  528. HRESULT
  529. CTransformInputPin::CheckStreaming() {
  530.     ASSERT(m_pTransformFilter->m_pOutput != NULL);
  531.     if(!m_pTransformFilter->m_pOutput->IsConnected()) {
  532.         return VFW_E_NOT_CONNECTED;
  533.     }
  534.     else {
  535.         //  Shouldn't be able to get any data if we're not connected!
  536.         ASSERT(IsConnected());
  537.  
  538.         //  we're flushing
  539.         if(m_bFlushing) {
  540.             return S_FALSE;
  541.         }
  542.         //  Don't process stuff in Stopped state
  543.         if(IsStopped()) {
  544.             return VFW_E_WRONG_STATE;
  545.         }
  546.         if(m_bRunTimeError) {
  547.             return VFW_E_RUNTIME_ERROR;
  548.         }
  549.         return S_OK;
  550.     }
  551. }
  552.  
  553.  
  554. // =================================================================
  555. // Implements the CTransformInputPin class
  556. // =================================================================
  557.  
  558.  
  559. // constructor
  560.  
  561. CTransformInputPin::CTransformInputPin(
  562.     TCHAR *pObjectName,
  563.     CTransformFilter *pTransformFilter,
  564.     HRESULT * phr,
  565.     LPCWSTR pName)
  566.     : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName) {
  567.     DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));
  568.     m_pTransformFilter = pTransformFilter;
  569. }
  570.  
  571. #ifdef UNICODE
  572. CTransformInputPin::CTransformInputPin(
  573.     CHAR *pObjectName,
  574.     CTransformFilter *pTransformFilter,
  575.     HRESULT * phr,
  576.     LPCWSTR pName)
  577.     : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName) {
  578.     DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));
  579.     m_pTransformFilter = pTransformFilter;
  580. }
  581. #endif
  582.  
  583. // provides derived filter a chance to grab extra interfaces
  584.  
  585. HRESULT
  586. CTransformInputPin::CheckConnect(IPin *pPin) {
  587.     HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_INPUT,pPin);
  588.     if(FAILED(hr)) {
  589.         return hr;
  590.     }
  591.     return CBaseInputPin::CheckConnect(pPin);
  592. }
  593.  
  594.  
  595. // provides derived filter a chance to release it's extra interfaces
  596.  
  597. HRESULT
  598. CTransformInputPin::BreakConnect() {
  599.     //  Can't disconnect unless stopped
  600.     ASSERT(IsStopped());
  601.     m_pTransformFilter->BreakConnect(PINDIR_INPUT);
  602.     return CBaseInputPin::BreakConnect();
  603. }
  604.  
  605.  
  606. // Let derived class know when the input pin is connected
  607.  
  608. HRESULT
  609. CTransformInputPin::CompleteConnect(IPin *pReceivePin) {
  610.     HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin);
  611.     if(FAILED(hr)) {
  612.         return hr;
  613.     }
  614.     return CBaseInputPin::CompleteConnect(pReceivePin);
  615. }
  616.  
  617.  
  618. // check that we can support a given media type
  619.  
  620. HRESULT
  621. CTransformInputPin::CheckMediaType(const CMediaType* pmt) {
  622.     // Check the input type
  623.  
  624.     HRESULT hr = m_pTransformFilter->CheckInputType(pmt);
  625.     if(S_OK != hr) {
  626.         return hr;
  627.     }
  628.  
  629.     // if the output pin is still connected, then we have
  630.     // to check the transform not just the input format
  631.  
  632.     if((m_pTransformFilter->m_pOutput != NULL) &&
  633.         (m_pTransformFilter->m_pOutput->IsConnected())) {
  634.         return m_pTransformFilter->CheckTransform(pmt,
  635.             &m_pTransformFilter->m_pOutput->CurrentMediaType());
  636.     }
  637.     else {
  638.         return hr;
  639.     }
  640. }
  641.  
  642.  
  643. // set the media type for this connection
  644.  
  645. HRESULT
  646. CTransformInputPin::SetMediaType(const CMediaType* mtIn) {
  647.     // Set the base class media type (should always succeed)
  648.     HRESULT hr = CBasePin::SetMediaType(mtIn);
  649.     if(FAILED(hr)) {
  650.         return hr;
  651.     }
  652.  
  653.     // check the transform can be done (should always succeed)
  654.     ASSERT(SUCCEEDED(m_pTransformFilter->CheckInputType(mtIn)));
  655.  
  656.     return m_pTransformFilter->SetMediaType(PINDIR_INPUT,mtIn);
  657. }
  658.  
  659.  
  660. // =================================================================
  661. // Implements IMemInputPin interface
  662. // =================================================================
  663.  
  664.  
  665. // provide EndOfStream that passes straight downstream
  666. // (there is no queued data)
  667. STDMETHODIMP
  668. CTransformInputPin::EndOfStream(void) {
  669.     CAutoLock lck(&m_pTransformFilter->m_csReceive);
  670.     HRESULT hr = CheckStreaming();
  671.     if(S_OK == hr) {
  672.         hr = m_pTransformFilter->EndOfStream();
  673.     }
  674.     return hr;
  675. }
  676.  
  677.  
  678. // enter flushing state. Call default handler to block Receives, then
  679. // pass to overridable method in filter
  680. STDMETHODIMP
  681. CTransformInputPin::BeginFlush(void) {
  682.     CAutoLock lck(&m_pTransformFilter->m_csFilter);
  683.     //  Are we actually doing anything?
  684.     ASSERT(m_pTransformFilter->m_pOutput != NULL);
  685.     if(!IsConnected() ||
  686.         !m_pTransformFilter->m_pOutput->IsConnected()) {
  687.         return VFW_E_NOT_CONNECTED;
  688.     }
  689.     HRESULT hr = CBaseInputPin::BeginFlush();
  690.     if(FAILED(hr)) {
  691.         return hr;
  692.     }
  693.  
  694.     return m_pTransformFilter->BeginFlush();
  695. }
  696.  
  697.  
  698. // leave flushing state.
  699. // Pass to overridable method in filter, then call base class
  700. // to unblock receives (finally)
  701. STDMETHODIMP
  702. CTransformInputPin::EndFlush(void) {
  703.     CAutoLock lck(&m_pTransformFilter->m_csFilter);
  704.     //  Are we actually doing anything?
  705.     ASSERT(m_pTransformFilter->m_pOutput != NULL);
  706.     if(!IsConnected() ||
  707.         !m_pTransformFilter->m_pOutput->IsConnected()) {
  708.         return VFW_E_NOT_CONNECTED;
  709.     }
  710.  
  711.     HRESULT hr = m_pTransformFilter->EndFlush();
  712.     if(FAILED(hr)) {
  713.         return hr;
  714.     }
  715.  
  716.     return CBaseInputPin::EndFlush();
  717. }
  718.  
  719.  
  720. // here's the next block of data from the stream.
  721. // AddRef it yourself if you need to hold it beyond the end
  722. // of this call.
  723.  
  724. HRESULT
  725. CTransformInputPin::Receive(IMediaSample * pSample) {
  726.     HRESULT hr;
  727.     CAutoLock lck(&m_pTransformFilter->m_csReceive);
  728.     ASSERT(pSample);
  729.  
  730.     // check all is well with the base class
  731.     hr = CBaseInputPin::Receive(pSample);
  732.     if(S_OK == hr) {
  733.         hr = m_pTransformFilter->Receive(pSample);
  734.     }
  735.     return hr;
  736. }
  737.  
  738.  
  739.  
  740.  
  741. // override to pass downstream
  742. STDMETHODIMP
  743. CTransformInputPin::NewSegment(
  744.     REFERENCE_TIME tStart,
  745.     REFERENCE_TIME tStop,
  746.     double dRate) {
  747.     //  Save the values in the pin
  748.     CBasePin::NewSegment(tStart, tStop, dRate);
  749.     return m_pTransformFilter->NewSegment(tStart, tStop, dRate);
  750. }
  751.  
  752.  
  753.  
  754.  
  755. // =================================================================
  756. // Implements the CTransformOutputPin class
  757. // =================================================================
  758.  
  759.  
  760. // constructor
  761.  
  762. CTransformOutputPin::CTransformOutputPin(
  763.     TCHAR *pObjectName,
  764.     CTransformFilter *pTransformFilter,
  765.     HRESULT * phr,
  766.     LPCWSTR pPinName)
  767.     : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),
  768.       m_pPosition(NULL) {
  769.     DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));
  770.     m_pTransformFilter = pTransformFilter;
  771.  
  772. }
  773.  
  774. #ifdef UNICODE
  775. CTransformOutputPin::CTransformOutputPin(
  776.     CHAR *pObjectName,
  777.     CTransformFilter *pTransformFilter,
  778.     HRESULT * phr,
  779.     LPCWSTR pPinName)
  780.     : CBaseOutputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pPinName),
  781.       m_pPosition(NULL) {
  782.     DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::CTransformOutputPin")));
  783.     m_pTransformFilter = pTransformFilter;
  784.  
  785. }
  786. #endif
  787.  
  788. // destructor
  789.  
  790. CTransformOutputPin::~CTransformOutputPin() {
  791.     DbgLog((LOG_TRACE,2,TEXT("CTransformOutputPin::~CTransformOutputPin")));
  792.  
  793.     if(m_pPosition) m_pPosition->Release();
  794. }
  795.  
  796.  
  797. // overriden to expose IMediaPosition and IMediaSeeking control interfaces
  798.  
  799. STDMETHODIMP
  800. CTransformOutputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  801.     CheckPointer(ppv,E_POINTER);
  802.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  803.     *ppv = NULL;
  804.  
  805.     if(riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {
  806.  
  807.         // we should have an input pin by now
  808.  
  809.         ASSERT(m_pTransformFilter->m_pInput != NULL);
  810.  
  811.         if(m_pPosition == NULL) {
  812.  
  813.             HRESULT hr = CreatePosPassThru(GetOwner(),
  814.                 FALSE,
  815.                 (IPin *)m_pTransformFilter->m_pInput,
  816.                 &m_pPosition);
  817.             if(FAILED(hr)) {
  818.                 return hr;
  819.             }
  820.         }
  821.         return m_pPosition->QueryInterface(riid, ppv);
  822.     }
  823.     else {
  824.         return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
  825.     }
  826. }
  827.  
  828.  
  829. // provides derived filter a chance to grab extra interfaces
  830.  
  831. HRESULT
  832. CTransformOutputPin::CheckConnect(IPin *pPin) {
  833.     // we should have an input connection first
  834.  
  835.     ASSERT(m_pTransformFilter->m_pInput != NULL);
  836.     if((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {
  837.         return E_UNEXPECTED;
  838.     }
  839.  
  840.     HRESULT hr = m_pTransformFilter->CheckConnect(PINDIR_OUTPUT,pPin);
  841.     if(FAILED(hr)) {
  842.         return hr;
  843.     }
  844.     return CBaseOutputPin::CheckConnect(pPin);
  845. }
  846.  
  847.  
  848. // provides derived filter a chance to release it's extra interfaces
  849.  
  850. HRESULT
  851. CTransformOutputPin::BreakConnect() {
  852.     //  Can't disconnect unless stopped
  853.     ASSERT(IsStopped());
  854.     m_pTransformFilter->BreakConnect(PINDIR_OUTPUT);
  855.     return CBaseOutputPin::BreakConnect();
  856. }
  857.  
  858.  
  859. // Let derived class know when the output pin is connected
  860.  
  861. HRESULT
  862. CTransformOutputPin::CompleteConnect(IPin *pReceivePin) {
  863.     HRESULT hr = m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin);
  864.     if(FAILED(hr)) {
  865.         return hr;
  866.     }
  867.     return CBaseOutputPin::CompleteConnect(pReceivePin);
  868. }
  869.  
  870.  
  871. // check a given transform - must have selected input type first
  872.  
  873. HRESULT
  874. CTransformOutputPin::CheckMediaType(const CMediaType* pmtOut) {
  875.     // must have selected input first
  876.     ASSERT(m_pTransformFilter->m_pInput != NULL);
  877.     if((m_pTransformFilter->m_pInput->IsConnected() == FALSE)) {
  878.         return E_INVALIDARG;
  879.     }
  880.  
  881.     return m_pTransformFilter->CheckTransform(&m_pTransformFilter->m_pInput->CurrentMediaType(),
  882.         pmtOut);
  883. }
  884.  
  885.  
  886. // called after we have agreed a media type to actually set it in which case
  887. // we run the CheckTransform function to get the output format type again
  888.  
  889. HRESULT
  890. CTransformOutputPin::SetMediaType(const CMediaType* pmtOut) {
  891.     HRESULT hr = NOERROR;
  892.     ASSERT(m_pTransformFilter->m_pInput != NULL);
  893.  
  894.     ASSERT(m_pTransformFilter->m_pInput->CurrentMediaType().IsValid());
  895.  
  896.     // Set the base class media type (should always succeed)
  897.     hr = CBasePin::SetMediaType(pmtOut);
  898.     if(FAILED(hr)) {
  899.         return hr;
  900.     }
  901.  
  902. #ifdef DEBUG
  903.     if(FAILED(m_pTransformFilter->CheckTransform(&m_pTransformFilter->
  904.         m_pInput->CurrentMediaType(),pmtOut))) {
  905.         DbgLog((LOG_ERROR,0,TEXT("*** This filter is accepting an output media type")));
  906.         DbgLog((LOG_ERROR,0,TEXT("    that it can't currently transform to.  I hope")));
  907.         DbgLog((LOG_ERROR,0,TEXT("    it's smart enough to reconnect its input.")));
  908.     }
  909. #endif
  910.  
  911.     return m_pTransformFilter->SetMediaType(PINDIR_OUTPUT,pmtOut);
  912. }
  913.  
  914.  
  915. // pass the buffer size decision through to the main transform class
  916.  
  917. HRESULT
  918. CTransformOutputPin::DecideBufferSize(
  919.     IMemAllocator * pAllocator,
  920.     ALLOCATOR_PROPERTIES* pProp) {
  921.     return m_pTransformFilter->DecideBufferSize(pAllocator, pProp);
  922. }
  923.  
  924.  
  925.  
  926. // return a specific media type indexed by iPosition
  927.  
  928. HRESULT
  929. CTransformOutputPin::GetMediaType(
  930.     int iPosition,
  931.     CMediaType *pMediaType) {
  932.     ASSERT(m_pTransformFilter->m_pInput != NULL);
  933.  
  934.     //  We don't have any media types if our input is not connected
  935.  
  936.     if(m_pTransformFilter->m_pInput->IsConnected()) {
  937.         return m_pTransformFilter->GetMediaType(iPosition,pMediaType);
  938.     }
  939.     else {
  940.         return VFW_S_NO_MORE_ITEMS;
  941.     }
  942. }
  943.  
  944.  
  945. // Override this if you can do something constructive to act on the
  946. // quality message.  Consider passing it upstream as well
  947.  
  948. // Pass the quality mesage on upstream.
  949.  
  950. STDMETHODIMP
  951. CTransformOutputPin::Notify(IBaseFilter * pSender, Quality q) {
  952.     UNREFERENCED_PARAMETER(pSender);
  953.     ValidateReadPtr(pSender,sizeof(IBaseFilter));
  954.  
  955.     // First see if we want to handle this ourselves
  956.     HRESULT hr = m_pTransformFilter->AlterQuality(q);
  957.     if(hr!=S_FALSE) {
  958.         return hr;        // either S_OK or a failure
  959.     }
  960.  
  961.     // S_FALSE means we pass the message on.
  962.     // Find the quality sink for our input pin and send it there
  963.  
  964.     ASSERT(m_pTransformFilter->m_pInput != NULL);
  965.  
  966.     return m_pTransformFilter->m_pInput->PassNotify(q);
  967.  
  968. } // Notify
  969.  
  970.  
  971. // the following removes a very large number of level 4 warnings from the microsoft
  972. // compiler output, which are not useful at all in this case.
  973. #pragma warning(disable:4514)
  974.  
  975.