//------------------------------------------------------------------------------ // 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 #include // ================================================================= // 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)