forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			1017 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1017 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //------------------------------------------------------------------------------
 | |
| // File: Transfrm.cpp
 | |
| //
 | |
| // Desc: DirectShow base classes - implements class for simple transform
 | |
| //       filters such as video decompressors.
 | |
| //
 | |
| // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
 | |
| //------------------------------------------------------------------------------
 | |
| 
 | |
| 
 | |
| #include <streams.h>
 | |
| #include <measure.h>
 | |
| 
 | |
| 
 | |
| // =================================================================
 | |
| // Implements the CTransformFilter class
 | |
| // =================================================================
 | |
| 
 | |
| CTransformFilter::CTransformFilter(__in_opt LPCTSTR pName,
 | |
|                                    __inout_opt 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(__in_opt LPCSTR pName,
 | |
|                                    __inout_opt 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, __deref_out 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, __deref_out 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(
 | |
|     __in_opt LPCTSTR pObjectName,
 | |
|     __inout CTransformFilter *pTransformFilter,
 | |
|     __inout HRESULT * phr,
 | |
|     __in_opt LPCWSTR pName)
 | |
|     : CBaseInputPin(pObjectName, pTransformFilter, &pTransformFilter->m_csFilter, phr, pName)
 | |
| {
 | |
|     DbgLog((LOG_TRACE,2,TEXT("CTransformInputPin::CTransformInputPin")));
 | |
|     m_pTransformFilter = pTransformFilter;
 | |
| }
 | |
| 
 | |
| #ifdef UNICODE
 | |
| CTransformInputPin::CTransformInputPin(
 | |
|     __in_opt LPCSTR pObjectName,
 | |
|     __inout CTransformFilter *pTransformFilter,
 | |
|     __inout HRESULT * phr,
 | |
|     __in_opt 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(
 | |
|     __in_opt LPCTSTR pObjectName,
 | |
|     __inout CTransformFilter *pTransformFilter,
 | |
|     __inout HRESULT * phr,
 | |
|     __in_opt 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(
 | |
|     __in_opt LPCSTR pObjectName,
 | |
|     __inout CTransformFilter *pTransformFilter,
 | |
|     __inout HRESULT * phr,
 | |
|     __in_opt 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, __deref_out 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,
 | |
|     __inout ALLOCATOR_PROPERTIES* pProp)
 | |
| {
 | |
|     return m_pTransformFilter->DecideBufferSize(pAllocator, pProp);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| // return a specific media type indexed by iPosition
 | |
| 
 | |
| HRESULT
 | |
| CTransformOutputPin::GetMediaType(
 | |
|     int iPosition,
 | |
|     __inout 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)
 |