/*
 * Implements IBaseFilter for transform filters. (internal)
 *
 * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include "config.h"

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
#include "strmif.h"
#include "control.h"
#include "vfwmsgs.h"
#include "uuids.h"

#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(quartz);

#include "quartz_private.h"
#include "xform.h"
#include "sample.h"


static const WCHAR XFORM_DefInName[] =
{'X','F','o','r','m',' ','I','n',0};
static const WCHAR XFORM_DefOutName[] =
{'X','F','o','r','m',' ','O','u','t',0};

/***************************************************************************
 *
 *	CTransformBaseImpl methods
 *
 */

static HRESULT CTransformBaseImpl_OnActive( CBaseFilterImpl* pImpl )
{
	CTransformBaseImpl_THIS(pImpl,basefilter);

	TRACE( "(%p)\n", This );

	return NOERROR;
}

static HRESULT CTransformBaseImpl_OnInactive( CBaseFilterImpl* pImpl )
{
	CTransformBaseImpl_THIS(pImpl,basefilter);
	HRESULT hr;
	IMemAllocator* pAllocator;

	TRACE( "(%p)\n", This );

	if ( This->pInPin->pin.pPinConnectedTo == NULL ||
		 This->pOutPin->pin.pPinConnectedTo == NULL )
		return NOERROR;

	EnterCriticalSection( &This->basefilter.csFilter );

	pAllocator = This->m_pOutPinAllocator;
	if ( pAllocator != NULL &&
		 This->pInPin->meminput.pAllocator != pAllocator )
	{
		hr = IMemAllocator_Commit( pAllocator );
		if ( FAILED(hr) )
			goto end;
	}

	if ( !This->m_bFiltering )
	{
		hr = This->m_pHandler->pBeginTransform( This, This->pInPin->pin.pmtConn, This->pOutPin->pin.pmtConn, This->m_bReuseSample );
		if ( FAILED(hr) )
			goto end;
		This->m_bFiltering = TRUE;
	}

	hr = NOERROR;
end:
	LeaveCriticalSection( &This->basefilter.csFilter );

	return hr;
}

static HRESULT CTransformBaseImpl_OnStop( CBaseFilterImpl* pImpl )
{
	CTransformBaseImpl_THIS(pImpl,basefilter);
	IMemAllocator* pAllocator;

	TRACE( "(%p)\n", This );

	EnterCriticalSection( &This->basefilter.csFilter );

	if ( This->m_bFiltering )
	{
		This->m_pHandler->pEndTransform( This );
		This->m_bFiltering = FALSE;
	}
	if ( This->m_pSample != NULL )
	{
		IMediaSample_Release( This->m_pSample );
		This->m_pSample = NULL;
	}

	pAllocator = This->m_pOutPinAllocator;
	if ( pAllocator != NULL &&
		 This->pInPin->meminput.pAllocator != pAllocator )
	{
		IMemAllocator_Decommit( pAllocator );
	}

	LeaveCriticalSection( &This->basefilter.csFilter );

	return NOERROR;
}

static const CBaseFilterHandlers filterhandlers =
{
	CTransformBaseImpl_OnActive, /* pOnActive */
	CTransformBaseImpl_OnInactive, /* pOnInactive */
	CTransformBaseImpl_OnStop, /* pOnStop */
};

/***************************************************************************
 *
 *	CTransformBaseInPinImpl methods
 *
 */

static HRESULT CTransformBaseInPinImpl_OnPostConnect( CPinBaseImpl* pImpl, IPin* pPin )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);
	HRESULT hr;

	TRACE( "(%p,%p)\n", This, pPin );

	EnterCriticalSection( &This->pFilter->basefilter.csFilter );
	hr = This->pFilter->m_pHandler->pGetOutputTypes( This->pFilter, This->pFilter->pInPin->pin.pmtConn, &This->pFilter->pOutPin->pin.pmtAcceptTypes, &This->pFilter->pOutPin->pin.cAcceptTypes );
	if ( FAILED(hr) )
		goto end;

	hr = NOERROR;
end:
	LeaveCriticalSection( &This->pFilter->basefilter.csFilter );

	return hr;
}

static HRESULT CTransformBaseInPinImpl_OnDisconnect( CPinBaseImpl* pImpl )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);

	TRACE( "(%p)\n", This );

	if ( This->meminput.pAllocator != NULL )
	{
		IMemAllocator_Decommit(This->meminput.pAllocator);
		IMemAllocator_Release(This->meminput.pAllocator);
		This->meminput.pAllocator = NULL;
	}

	return NOERROR;
}

static HRESULT CTransformBaseInPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);
	HRESULT hr;

	TRACE( "(%p,%p)\n", This, pmt );

	EnterCriticalSection( &This->pFilter->basefilter.csFilter );
	hr = This->pFilter->m_pHandler->pCheckMediaType( This->pFilter, pmt, (This->pFilter->pOutPin->pin.pPinConnectedTo != NULL) ? This->pFilter->pOutPin->pin.pmtConn : NULL );
	LeaveCriticalSection( &This->pFilter->basefilter.csFilter );

	return hr;
}

static HRESULT CTransformBaseInPinImpl_Receive( CPinBaseImpl* pImpl, IMediaSample* pSample )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);
	HRESULT hr;

	TRACE( "(%p,%p)\n", This, pSample );

	if ( This->pin.pPinConnectedTo == NULL ||
		 This->pFilter->pOutPin->pin.pPinConnectedTo == NULL )
		return NOERROR;

	if ( !This->pFilter->m_bFiltering )
		return E_UNEXPECTED;

	if ( This->pFilter->m_bInFlush )
		return S_FALSE;

	if ( This->pFilter->m_pHandler->pProcessReceive != NULL )
	{
		hr = This->pFilter->m_pHandler->pProcessReceive( This->pFilter, pSample );
	}
	else
	{
		if ( This->meminput.pAllocator != This->pFilter->m_pOutPinAllocator )
		{
			if ( This->pFilter->m_pSample == NULL )
			{
				hr = IMemAllocator_GetBuffer( This->pFilter->m_pOutPinAllocator, &This->pFilter->m_pSample, NULL, NULL, 0 );
				if ( FAILED(hr) )
					goto end;
			}
			hr = QUARTZ_IMediaSample_Copy(
				This->pFilter->m_pSample, pSample, This->pFilter->m_bPreCopy );
			if ( FAILED(hr) )
				goto end;
		}

		if ( This->pFilter->m_bPreCopy )
			hr = This->pFilter->m_pHandler->pTransform( This->pFilter, This->pFilter->m_pSample, NULL );
		else
			hr = This->pFilter->m_pHandler->pTransform( This->pFilter, pSample, This->pFilter->m_pSample );

		if ( FAILED(hr) )
			goto end;

		if ( hr == NOERROR )
		{
			hr = CPinBaseImpl_SendSample(&This->pFilter->pOutPin->pin,This->pFilter->m_pSample);
			if ( FAILED(hr) )
				goto end;
		}

		hr = NOERROR;
	end:
		if ( !This->pFilter->m_bReuseSample )
		{
			if ( This->pFilter->m_pSample != NULL )
			{
				IMediaSample_Release( This->pFilter->m_pSample );
				This->pFilter->m_pSample = NULL;
			}
		}

		if ( FAILED(hr) )
		{
			/* Notify(ABORT) */
		}
	}

	return hr;
}

static HRESULT CTransformBaseInPinImpl_ReceiveCanBlock( CPinBaseImpl* pImpl )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);

	TRACE( "(%p)\n", This );

	if ( This->pin.pPinConnectedTo == NULL ||
		 This->pFilter->pOutPin->pin.pPinConnectedTo == NULL )
		return S_FALSE;

	return CPinBaseImpl_SendReceiveCanBlock( &This->pFilter->pOutPin->pin );
}

static HRESULT CTransformBaseInPinImpl_EndOfStream( CPinBaseImpl* pImpl )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);

	TRACE( "(%p)\n", This );

	if ( This->pin.pPinConnectedTo == NULL ||
		 This->pFilter->pOutPin->pin.pPinConnectedTo == NULL )
		return NOERROR;

	return CPinBaseImpl_SendEndOfStream( &This->pFilter->pOutPin->pin );
}

static HRESULT CTransformBaseInPinImpl_BeginFlush( CPinBaseImpl* pImpl )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);

	TRACE( "(%p)\n", This );

	if ( This->pin.pPinConnectedTo == NULL ||
		 This->pFilter->pOutPin->pin.pPinConnectedTo == NULL )
		return NOERROR;

	This->pFilter->m_bInFlush = TRUE;

	return CPinBaseImpl_SendBeginFlush( &This->pFilter->pOutPin->pin );
}

static HRESULT CTransformBaseInPinImpl_EndFlush( CPinBaseImpl* pImpl )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);

	TRACE( "(%p)\n", This );

	if ( This->pin.pPinConnectedTo == NULL ||
		 This->pFilter->pOutPin->pin.pPinConnectedTo == NULL )
		return NOERROR;

	This->pFilter->m_bInFlush = FALSE;

	return CPinBaseImpl_SendEndFlush( &This->pFilter->pOutPin->pin );
}

static HRESULT CTransformBaseInPinImpl_NewSegment( CPinBaseImpl* pImpl, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double rate )
{
	CTransformBaseInPinImpl_THIS(pImpl,pin);

	FIXME( "(%p)\n", This );

	if ( This->pin.pPinConnectedTo == NULL ||
		 This->pFilter->pOutPin->pin.pPinConnectedTo == NULL )
		return NOERROR;

	return CPinBaseImpl_SendNewSegment( &This->pFilter->pOutPin->pin,
		rtStart, rtStop, rate );
}

static const CBasePinHandlers inputpinhandlers =
{
	NULL, /* pOnPreConnect */
	CTransformBaseInPinImpl_OnPostConnect, /* pOnPostConnect */
	CTransformBaseInPinImpl_OnDisconnect, /* pOnDisconnect */
	CTransformBaseInPinImpl_CheckMediaType, /* pCheckMediaType */
	NULL, /* pQualityNotify */
	CTransformBaseInPinImpl_Receive, /* pReceive */
	CTransformBaseInPinImpl_ReceiveCanBlock, /* pReceiveCanBlock */
	CTransformBaseInPinImpl_EndOfStream, /* pEndOfStream */
	CTransformBaseInPinImpl_BeginFlush, /* pBeginFlush */
	CTransformBaseInPinImpl_EndFlush, /* pEndFlush */
	CTransformBaseInPinImpl_NewSegment, /* pNewSegment */
};

/***************************************************************************
 *
 *	CTransformBaseOutPinImpl methods
 *
 */

static HRESULT CTransformBaseOutPinImpl_OnPostConnect( CPinBaseImpl* pImpl, IPin* pPin )
{
	CTransformBaseOutPinImpl_THIS(pImpl,pin);
	HRESULT hr;
	ALLOCATOR_PROPERTIES propReqThis;
	ALLOCATOR_PROPERTIES propReqPeer;
	ALLOCATOR_PROPERTIES propActual;
	BOOL bTransInPlace = FALSE;
	BOOL bTryToReUseSample = FALSE;
	BOOL bOutReadonly = FALSE;
	IMemAllocator*	pAllocator;

	FIXME( "(%p,%p)\n", This, pPin );

	if ( This->pFilter->pInPin->pin.pPinConnectedTo == NULL )
		return E_FAIL;
	if ( This->pin.pMemInputPinConnectedTo == NULL )
		return E_UNEXPECTED;

	ZeroMemory( &propReqThis, sizeof(ALLOCATOR_PROPERTIES) );
	ZeroMemory( &propReqPeer, sizeof(ALLOCATOR_PROPERTIES) );
	ZeroMemory( &propActual, sizeof(ALLOCATOR_PROPERTIES) );

	hr = This->pFilter->m_pHandler->pGetAllocProp( This->pFilter, This->pFilter->pInPin->pin.pmtConn, This->pin.pmtConn, &propReqThis, &bTransInPlace, &bTryToReUseSample );
	if ( FAILED(hr) )
		goto end;

	if ( propReqThis.cbAlign == 0 )
		propReqThis.cbAlign = 1;

	if ( bTransInPlace )
	{
		ZeroMemory( &propReqPeer, sizeof(ALLOCATOR_PROPERTIES) );
		hr = IMemInputPin_GetAllocatorRequirements(
			This->pin.pMemInputPinConnectedTo, &propReqPeer );
		if ( propReqPeer.cbAlign != 0 && propReqPeer.cbAlign != 1 )
			bTransInPlace = FALSE;
		if ( propReqPeer.cbPrefix != 0 )
			bTransInPlace = FALSE;

		bOutReadonly = FALSE;
		if ( bTransInPlace && This->pFilter->pInPin->meminput.bReadonly )
			bOutReadonly = TRUE;

		pAllocator = This->pFilter->pInPin->meminput.pAllocator;

		hr = IMemInputPin_NotifyAllocator(
			This->pin.pMemInputPinConnectedTo,
			pAllocator, bOutReadonly );
		if ( hr == NOERROR )
		{
			This->pFilter->m_pOutPinAllocator = pAllocator;
			IMemAllocator_AddRef(pAllocator);
			bTryToReUseSample = FALSE;
			goto end;
		}
	}

	hr = IMemInputPin_GetAllocator(
			This->pin.pMemInputPinConnectedTo, &pAllocator );
	if ( FAILED(hr) )
		goto end;
	hr = IMemAllocator_SetProperties( pAllocator, &propReqThis, &propActual );
	if ( SUCCEEDED(hr) )
	{
		TRACE("cBuffers = %ld / cbBuffer = %ld\n",propActual.cBuffers,propActual.cbBuffer);
		hr = IMemInputPin_NotifyAllocator(
			This->pin.pMemInputPinConnectedTo, pAllocator,
			bTryToReUseSample );
	}
	if ( FAILED(hr) )
	{
		IMemAllocator_Release(pAllocator);
		goto end;
	}
	This->pFilter->m_pOutPinAllocator = pAllocator;

	hr = NOERROR;
end:
	This->pFilter->m_bPreCopy = FALSE;
	This->pFilter->m_bReuseSample = FALSE;
	if ( hr == NOERROR )
	{
		This->pFilter->m_bPreCopy = bTransInPlace && (This->pFilter->pInPin->meminput.pAllocator != This->pFilter->m_pOutPinAllocator);
		This->pFilter->m_bReuseSample = bTryToReUseSample;
	}

	return hr;
}

static HRESULT CTransformBaseOutPinImpl_OnDisconnect( CPinBaseImpl* pImpl )
{
	CTransformBaseOutPinImpl_THIS(pImpl,pin);

	TRACE( "(%p)\n", This );

	if ( This->pFilter->m_pOutPinAllocator != NULL )
	{
		IMemAllocator_Decommit(This->pFilter->m_pOutPinAllocator);
		IMemAllocator_Release(This->pFilter->m_pOutPinAllocator);
		This->pFilter->m_pOutPinAllocator = NULL;
	}

	return NOERROR;
}

static HRESULT CTransformBaseOutPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt )
{
	CTransformBaseOutPinImpl_THIS(pImpl,pin);
	HRESULT hr;

	TRACE( "(%p,%p)\n", This, pmt );

	if ( This->pFilter->pInPin->pin.pPinConnectedTo == NULL )
		return E_FAIL;

	EnterCriticalSection( &This->pFilter->basefilter.csFilter );
	hr = This->pFilter->m_pHandler->pCheckMediaType( This->pFilter, This->pFilter->pInPin->pin.pmtConn, pmt );
	LeaveCriticalSection( &This->pFilter->basefilter.csFilter );

	return hr;
}

static const CBasePinHandlers outputpinhandlers =
{
	NULL, /* pOnPreConnect */
	CTransformBaseOutPinImpl_OnPostConnect, /* pOnPostConnect */
	CTransformBaseOutPinImpl_OnDisconnect, /* pOnDisconnect */
	CTransformBaseOutPinImpl_CheckMediaType, /* pCheckMediaType */
	NULL, /* pQualityNotify */
	OutputPinSync_Receive, /* pReceive */
	OutputPinSync_ReceiveCanBlock, /* pReceiveCanBlock */
	OutputPinSync_EndOfStream, /* pEndOfStream */
	OutputPinSync_BeginFlush, /* pBeginFlush */
	OutputPinSync_EndFlush, /* pEndFlush */
	OutputPinSync_NewSegment, /* pNewSegment */
};


/***************************************************************************
 *
 *	new/delete CTransformBaseImpl
 *
 */

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry FilterIFEntries[] =
{
  { &IID_IPersist, offsetof(CTransformBaseImpl,basefilter)-offsetof(CTransformBaseImpl,unk) },
  { &IID_IMediaFilter, offsetof(CTransformBaseImpl,basefilter)-offsetof(CTransformBaseImpl,unk) },
  { &IID_IBaseFilter, offsetof(CTransformBaseImpl,basefilter)-offsetof(CTransformBaseImpl,unk) },
};

static void QUARTZ_DestroyTransformBase(IUnknown* punk)
{
	CTransformBaseImpl_THIS(punk,unk);

	TRACE( "(%p)\n", This );

	This->m_pHandler->pCleanup(This);

	if ( This->pInPin != NULL )
	{
		IUnknown_Release(This->pInPin->unk.punkControl);
		This->pInPin = NULL;
	}
	if ( This->pOutPin != NULL )
	{
		IUnknown_Release(This->pOutPin->unk.punkControl);
		This->pOutPin = NULL;
	}
	if ( This->pSeekPass != NULL )
	{
		IUnknown_Release((IUnknown*)&This->pSeekPass->unk);
		This->pSeekPass = NULL;
	}

	CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);

	DeleteCriticalSection( &This->csReceive );
}

HRESULT QUARTZ_CreateTransformBase(
	IUnknown* punkOuter,void** ppobj,
	const CLSID* pclsidTransformBase,
	LPCWSTR pwszTransformBaseName,
	LPCWSTR pwszInPinName,
	LPCWSTR pwszOutPinName,
	const TransformBaseHandlers* pHandler )
{
	CTransformBaseImpl*	This = NULL;
	HRESULT hr;

	TRACE("(%p,%p)\n",punkOuter,ppobj);

	if ( pwszInPinName == NULL )
		pwszInPinName = XFORM_DefInName;
	if ( pwszOutPinName == NULL )
		pwszOutPinName = XFORM_DefOutName;

	This = (CTransformBaseImpl*)
		QUARTZ_AllocObj( sizeof(CTransformBaseImpl) );
	if ( This == NULL )
		return E_OUTOFMEMORY;

	This->pInPin = NULL;
	This->pOutPin = NULL;
	This->pSeekPass = NULL;
	This->m_pOutPinAllocator = NULL;
	This->m_bPreCopy = FALSE; /* sample must be copied */
	This->m_bReuseSample = FALSE; /* sample must be reused */
	This->m_bInFlush = FALSE;
	This->m_pSample = NULL;
	This->m_bFiltering = FALSE;
	This->m_pHandler = pHandler;
	This->m_pUserData = NULL;

	QUARTZ_IUnkInit( &This->unk, punkOuter );

	hr = CBaseFilterImpl_InitIBaseFilter(
		&This->basefilter,
		This->unk.punkControl,
		pclsidTransformBase,
		pwszTransformBaseName,
		&filterhandlers );
	if ( SUCCEEDED(hr) )
	{
		/* construct this class. */
		hr = This->m_pHandler->pInit( This );
		if ( FAILED(hr) )
		{
			CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
		}
	}

	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj(This);
		return hr;
	}

	This->unk.pEntries = FilterIFEntries;
	This->unk.dwEntries = sizeof(FilterIFEntries)/sizeof(FilterIFEntries[0]);
	This->unk.pOnFinalRelease = QUARTZ_DestroyTransformBase;
	InitializeCriticalSection( &This->csReceive );

	/* create pins. */
	hr = QUARTZ_CreateTransformBaseInPin(
		This, &This->basefilter.csFilter, &This->csReceive,
		&This->pInPin, pwszInPinName );
	if ( SUCCEEDED(hr) )
		hr = QUARTZ_CompList_AddComp(
			This->basefilter.pInPins,
			(IUnknown*)&(This->pInPin->pin),
			NULL, 0 );
	if ( SUCCEEDED(hr) )
		hr = QUARTZ_CreateTransformBaseOutPin(
			This, &This->basefilter.csFilter,
			&This->pOutPin, pwszOutPinName );
	if ( SUCCEEDED(hr) )
		hr = QUARTZ_CompList_AddComp(
			This->basefilter.pOutPins,
			(IUnknown*)&(This->pOutPin->pin),
			NULL, 0 );

	if ( SUCCEEDED(hr) )
	{
		hr = QUARTZ_CreateSeekingPassThruInternal(
			(IUnknown*)&(This->pOutPin->unk), &This->pSeekPass,
			FALSE, (IPin*)&(This->pInPin->pin) );
	}

	if ( FAILED(hr) )
	{
		IUnknown_Release( This->unk.punkControl );
		return hr;
	}

	*ppobj = (void*)&(This->unk);

	return S_OK;
}

/***************************************************************************
 *
 *	new/delete CTransformBaseInPinImpl
 *
 */

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry InPinIFEntries[] =
{
  { &IID_IPin, offsetof(CTransformBaseInPinImpl,pin)-offsetof(CTransformBaseInPinImpl,unk) },
  { &IID_IMemInputPin, offsetof(CTransformBaseInPinImpl,meminput)-offsetof(CTransformBaseInPinImpl,unk) },
};

static void QUARTZ_DestroyTransformBaseInPin(IUnknown* punk)
{
	CTransformBaseInPinImpl_THIS(punk,unk);

	TRACE( "(%p)\n", This );

	CPinBaseImpl_UninitIPin( &This->pin );
	CMemInputPinBaseImpl_UninitIMemInputPin( &This->meminput );
}

HRESULT QUARTZ_CreateTransformBaseInPin(
	CTransformBaseImpl* pFilter,
	CRITICAL_SECTION* pcsPin,
	CRITICAL_SECTION* pcsPinReceive,
	CTransformBaseInPinImpl** ppPin,
	LPCWSTR pwszPinName )
{
	CTransformBaseInPinImpl*	This = NULL;
	HRESULT hr;

	TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin);

	This = (CTransformBaseInPinImpl*)
		QUARTZ_AllocObj( sizeof(CTransformBaseInPinImpl) );
	if ( This == NULL )
		return E_OUTOFMEMORY;

	QUARTZ_IUnkInit( &This->unk, NULL );
	This->pFilter = pFilter;

	hr = CPinBaseImpl_InitIPin(
		&This->pin,
		This->unk.punkControl,
		pcsPin, pcsPinReceive,
		&pFilter->basefilter,
		pwszPinName,
		FALSE,
		&inputpinhandlers );

	if ( SUCCEEDED(hr) )
	{
		hr = CMemInputPinBaseImpl_InitIMemInputPin(
			&This->meminput,
			This->unk.punkControl,
			&This->pin );
		if ( FAILED(hr) )
		{
			CPinBaseImpl_UninitIPin( &This->pin );
		}
	}

	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj(This);
		return hr;
	}

	This->unk.pEntries = InPinIFEntries;
	This->unk.dwEntries = sizeof(InPinIFEntries)/sizeof(InPinIFEntries[0]);
	This->unk.pOnFinalRelease = QUARTZ_DestroyTransformBaseInPin;

	*ppPin = This;

	TRACE("returned successfully.\n");

	return S_OK;
}


/***************************************************************************
 *
 *	new/delete CTransformBaseOutPinImpl
 *
 */

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry OutPinIFEntries[] =
{
  { &IID_IPin, offsetof(CTransformBaseOutPinImpl,pin)-offsetof(CTransformBaseOutPinImpl,unk) },
  { &IID_IQualityControl, offsetof(CTransformBaseOutPinImpl,qcontrol)-offsetof(CTransformBaseOutPinImpl,unk) },
};

static HRESULT CTransformBaseOutPinImpl_OnQueryInterface(
	IUnknown* punk, const IID* piid, void** ppobj )
{
	CTransformBaseOutPinImpl_THIS(punk,unk);

	if ( This->pFilter == NULL || This->pFilter->pSeekPass == NULL )
		return E_NOINTERFACE;

	if ( IsEqualGUID( &IID_IMediaPosition, piid ) ||
		 IsEqualGUID( &IID_IMediaSeeking, piid ) )
	{
		TRACE( "IMediaSeeking(or IMediaPosition) is queried\n" );
		return IUnknown_QueryInterface( (IUnknown*)(&This->pFilter->pSeekPass->unk), piid, ppobj );
	}

	return E_NOINTERFACE;
}

static void QUARTZ_DestroyTransformBaseOutPin(IUnknown* punk)
{
	CTransformBaseOutPinImpl_THIS(punk,unk);

	TRACE( "(%p)\n", This );

	CPinBaseImpl_UninitIPin( &This->pin );
	CQualityControlPassThruImpl_UninitIQualityControl( &This->qcontrol );
}

HRESULT QUARTZ_CreateTransformBaseOutPin(
	CTransformBaseImpl* pFilter,
	CRITICAL_SECTION* pcsPin,
	CTransformBaseOutPinImpl** ppPin,
	LPCWSTR pwszPinName )
{
	CTransformBaseOutPinImpl*	This = NULL;
	HRESULT hr;

	TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin);

	This = (CTransformBaseOutPinImpl*)
		QUARTZ_AllocObj( sizeof(CTransformBaseOutPinImpl) );
	if ( This == NULL )
		return E_OUTOFMEMORY;

	QUARTZ_IUnkInit( &This->unk, NULL );
	This->qiext.pNext = NULL;
	This->qiext.pOnQueryInterface = &CTransformBaseOutPinImpl_OnQueryInterface;
	QUARTZ_IUnkAddDelegation( &This->unk, &This->qiext );

	This->pFilter = pFilter;

	hr = CPinBaseImpl_InitIPin(
		&This->pin,
		This->unk.punkControl,
		pcsPin, NULL,
		&pFilter->basefilter,
		pwszPinName,
		TRUE,
		&outputpinhandlers );

	if ( SUCCEEDED(hr) )
	{
		hr = CQualityControlPassThruImpl_InitIQualityControl(
			&This->qcontrol,
			This->unk.punkControl,
			&This->pin );
		if ( FAILED(hr) )
		{
			CPinBaseImpl_UninitIPin( &This->pin );
		}
	}

	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj(This);
		return hr;
	}

	This->unk.pEntries = OutPinIFEntries;
	This->unk.dwEntries = sizeof(OutPinIFEntries)/sizeof(OutPinIFEntries[0]);
	This->unk.pOnFinalRelease = QUARTZ_DestroyTransformBaseOutPin;

	*ppPin = This;

	TRACE("returned successfully.\n");

	return S_OK;
}