| //------------------------------------------------------------------------------ | |
| // 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) |