mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-04-02 10:52:54 -04:00
Update it to the version found at https://github.com/Microsoft/Windows-classic-samples , which is in an MIT licensed repo, and add the LICENSE file (edited to remove the SIL Open Font LICENSE part since that doesn't apply). Some modifications have been made to reduce the diff/stop git complaining (not including any file that wasn't in the previous version and removing the related header includes in streams.h, and fixing some but not all of the whitespace issues).
1016 lines
27 KiB
C++
1016 lines
27 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)
|