/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 *
 * This file is part of mediastreamer2
 * (see https://gitlab.linphone.org/BC/public/mediastreamer2).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef VIDEO_ENABLED

#if !defined(_WIN32_WCE) // Allready defined for wince
#define UNICODE
#endif

#include <initguid.h>
#include <streams.h>

#if !defined(_WIN32_WCE)
EXTERN_C const CLSID CLSID_NullRenderer;
#endif
#include "dxfilter.h"

#pragma warning(disable : 4800)

const AMOVIESETUP_PIN psudDXFilterPins[] = {{
                                                L"Input" // strName
                                                ,
                                                FALSE // bRendered
                                                ,
                                                FALSE // bOutput
                                                ,
                                                FALSE // bZero
                                                ,
                                                FALSE // bMany
                                                ,
                                                &CLSID_NULL // clsConnectsToFilter
                                                ,
                                                L"" // strConnectsToPin
                                                ,
                                                0 // nTypes
                                                ,
                                                NULL // lpTypes
                                            },
                                            {
                                                L"Output" // strName
                                                ,
                                                FALSE // bRendered
                                                ,
                                                TRUE // bOutput
                                                ,
                                                FALSE // bZero
                                                ,
                                                FALSE // bMany
                                                ,
                                                &CLSID_NULL // clsConnectsToFilter
                                                ,
                                                L"" // strConnectsToPin
                                                ,
                                                0 // nTypes
                                                ,
                                                NULL // lpTypes
                                            }};

const AMOVIESETUP_FILTER sudDXFilter = {&CLSID_DXFilter // clsID
                                        ,
                                        L"DXFilter for mediastreamer2" // strName
                                        ,
                                        MERIT_DO_NOT_USE // dwMerit
                                        ,
                                        2 // nPins
                                        ,
                                        psudDXFilterPins}; // lpPin

// Needed for the CreateInstance mechanism
CFactoryTemplate g_Templates[] = {
    {L"DirectX Filter for mediastreamer2", &CLSID_DXFilter, CDXFilter::CreateInstance, NULL, &sudDXFilter}

};

int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

////////////////////////////////////////////////////////////////////////
//
// Exported entry points for registration and unregistration
// (in this case they only call through to default implementations).
//
////////////////////////////////////////////////////////////////////////

STDAPI DllRegisterServer() {
	return AMovieDllRegisterServer2(TRUE);
}

STDAPI DllUnregisterServer() {
	return AMovieDllRegisterServer2(FALSE);
}

//
// DllEntryPoint
//
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) {
	return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}

//
// CreateInstance
//
// Provide the way for COM to create a CDXFilter object
//
CUnknown *WINAPI CDXFilter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) {
	/* ASSERT(phr); */

	// assuming we don't want to modify the data
	CDXFilter *pNewObject = new CDXFilter(punk, phr, FALSE);

	if (pNewObject == NULL) {
		if (phr) *phr = E_OUTOFMEMORY;
	}

	return pNewObject;

} // CreateInstance

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------

CDXFilter::CDXFilter(IUnknown *pOuter, HRESULT *phr, BOOL ModifiesData)
    : CTransInPlaceFilter(TEXT("DXFilter"),
                          (IUnknown *)pOuter,
                          CLSID_DXFilter,
                          phr
#if !defined(_WIN32_WCE)
                          ,
                          (BOOL)ModifiesData
#endif
                          ),
      m_callback(NULL) {
	// this is used to override the input pin with our own
	m_pInput = (CTransInPlaceInputPin *)new CDXFilterInPin(this, phr);
	if (!m_pInput) {
		if (phr) *phr = E_OUTOFMEMORY;
	}

	// Ensure that the output pin gets created.  This is necessary because our
	// SetDeliveryBuffer() method assumes that the input/output pins are created, but
	// the output pin isn't created until GetPin() is called.  The
	// CTransInPlaceFilter::GetPin() method will create the output pin, since we
	// have not already created one.
	IPin *pOutput = GetPin(1);
	// The pointer is not AddRef'ed by GetPin(), so don't release it
}

STDMETHODIMP CDXFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
	CheckPointer(ppv, E_POINTER);

	if (riid == IID_IDXFilter) {
		return GetInterface((IDXFilter *)this, ppv);
	} else {
		return CTransInPlaceFilter::NonDelegatingQueryInterface(riid, ppv);
	}
}

//----------------------------------------------------------------------------
// This is where you force the sample grabber to connect with one type
// or the other. What you do here is crucial to what type of data your
// app will be dealing with in the sample grabber's callback. For instance,
// if you don't enforce right-side-up video in this call, you may not get
// right-side-up video in your callback. It all depends on what you do here.
//----------------------------------------------------------------------------

HRESULT CDXFilter::CheckInputType(const CMediaType *pmt) {
	CheckPointer(pmt, E_POINTER);
	CAutoLock lock(&m_Lock);

	// if the major type is not set, then accept anything

	GUID g = *m_mtAccept.Type();
	if (g == GUID_NULL) {
		return NOERROR;
	}

	// if the major type is set, don't accept anything else

	if (g != *pmt->Type()) {
		return VFW_E_INVALID_MEDIA_TYPE;
	}

	// subtypes must match, if set. if not set, accept anything
	VIDEOINFO *pvi = (VIDEOINFO *)pmt->Format();

	g = *m_mtAccept.Subtype();
	if (g == GUID_NULL) {
		return NOERROR;
	}
#if 0
	if( MEDIASUBTYPE_RGB24 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_YVU9 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_Y411 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_Y41P == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_YUY2 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_YVYU == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_UYVY == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_Y211 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_YV12 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_CLJR == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_IF09 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_CPLA == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_MJPG == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_TVMJ == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_WAKE == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_CFCC == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_IJPG == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_Plum == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB1 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB1 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB1 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB4 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB8 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB565 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB555 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB24 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_RGB32 == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_Overlay == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_MPEG1Packet == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_MPEG1Payload == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_MPEG1AudioPayload == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIATYPE_MPEG1SystemStream == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_MPEG1System == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_MPEG1VideoCD == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_MPEG1Video == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_Avi == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_Asf == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_QTMovie == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_QTRpza == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_QTSmc == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_QTRle == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_QTJpeg == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_PCM == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_WAVE == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_AU == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_AIFF == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_DssVideo == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_VPVideo == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_VPVBI == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_VPVideo == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_VPVideo == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_VPVideo == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_VPVideo == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
    if( MEDIASUBTYPE_VPVideo == *pmt->Subtype( ) )
        return VFW_E_INVALID_MEDIA_TYPE;
#endif

	if (g != *pmt->Subtype()) {
		return VFW_E_INVALID_MEDIA_TYPE;
	}

	// format types must match, if one is set

	g = *m_mtAccept.FormatType();
	if (g == GUID_NULL) {
		return NOERROR;
	}
	if (g != *pmt->FormatType()) {
		return VFW_E_INVALID_MEDIA_TYPE;
	}

	// at this point, for this sample code, this is good enough,
	// but you may want to make it more strict

	// compare sizes
	// VIDEOINFO *pvi = (VIDEOINFO *)pmt->Format();
	VIDEOINFO *pvi2 = (VIDEOINFO *)m_mtAccept.Format();
	if (pvi2 == NULL) return NOERROR;

	if (pvi == NULL) return VFW_E_INVALID_MEDIA_TYPE;
#if !defined(_WIN32_WCE)
	if (pvi->bmiHeader.biCompression != pvi2->bmiHeader.biCompression) return VFW_E_INVALID_MEDIA_TYPE;
#endif
	if (pvi->bmiHeader.biBitCount != pvi2->bmiHeader.biBitCount) return VFW_E_INVALID_MEDIA_TYPE;
	if (pvi->bmiHeader.biWidth != pvi2->bmiHeader.biWidth) return VFW_E_INVALID_MEDIA_TYPE;
	if (pvi->bmiHeader.biHeight != pvi2->bmiHeader.biHeight) return VFW_E_INVALID_MEDIA_TYPE;
	if (pvi->bmiHeader.biSizeImage != pvi2->bmiHeader.biSizeImage) return VFW_E_INVALID_MEDIA_TYPE;

	return NOERROR;
}

//----------------------------------------------------------------------------
// This bit is almost straight out of the base classes.
// We override this so we can handle Transform( )'s error
// result differently.
//----------------------------------------------------------------------------

HRESULT CDXFilter::Receive(IMediaSample *pms) {
	CheckPointer(pms, E_POINTER);

	HRESULT hr;
	AM_SAMPLE2_PROPERTIES *const pProps = m_pInput->SampleProps();

	if (pProps->dwStreamId != AM_STREAM_MEDIA) {
		if (m_pOutput->IsConnected()) return m_pOutput->Deliver(pms);
		else return NOERROR;
	}

#if !defined(_WIN32_WCE)
	if (UsingDifferentAllocators()) {
		// We have to copy the data.

		pms = Copy(pms);

		if (pms == NULL) {
			return E_UNEXPECTED;
		}
	}
#endif

	// have the derived class transform the data
	hr = Transform(pms);

	if (FAILED(hr)) {
		// DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace")));
#if !defined(_WIN32_WCE)
		if (UsingDifferentAllocators()) {
			pms->Release();
		}
#endif
		return hr;
	}

	if (hr == NOERROR) {
		hr = m_pOutput->Deliver(pms);
	}

	// release the output buffer. If the connected pin still needs it,
	// it will have addrefed it itself.
#if !defined(_WIN32_WCE)
	if (UsingDifferentAllocators()) {
		pms->Release();
	}
#endif
	return hr;
}

//----------------------------------------------------------------------------
// Transform
//----------------------------------------------------------------------------

HRESULT CDXFilter::Transform(IMediaSample *pms) {
	CheckPointer(pms, E_POINTER);
	CAutoLock lock(&m_Lock);

	if (m_callback) {
		REFERENCE_TIME StartTime, StopTime;
		pms->GetTime(&StartTime, &StopTime);

		StartTime += m_pInput->CurrentStartTime();
		StopTime += m_pInput->CurrentStartTime();

		BOOL *pTypeChanged = &((CDXFilterInPin *)m_pInput)->m_bMediaTypeChanged;

		HRESULT hr = m_callback(pms, &StartTime, &StopTime, *pTypeChanged);

		*pTypeChanged = FALSE; // now that we notified user, we can clear it

		return hr;
	}

	return NOERROR;
}

//----------------------------------------------------------------------------
// SetAcceptedMediaType
//----------------------------------------------------------------------------

STDMETHODIMP CDXFilter::SetAcceptedMediaType(const CMediaType *pmt) {
	CAutoLock lock(&m_Lock);

	if (!pmt) {
		m_mtAccept = CMediaType();
		return NOERROR;
	}

	HRESULT hr;
#if !defined(_WIN32_WCE)
	hr = CopyMediaType(&m_mtAccept, pmt);
#else
	hr = S_OK;
	CopyMediaType(&m_mtAccept, pmt);
#endif
	return hr;
}

//----------------------------------------------------------------------------
// GetAcceptedMediaType
//----------------------------------------------------------------------------

STDMETHODIMP CDXFilter::GetConnectedMediaType(CMediaType *pmt) {
	if (!m_pInput || !m_pInput->IsConnected()) {
		return VFW_E_NOT_CONNECTED;
	}

	return m_pInput->ConnectionMediaType(pmt);
}

//----------------------------------------------------------------------------
// SetCallback
//----------------------------------------------------------------------------

STDMETHODIMP CDXFilter::SetCallback(SAMPLECALLBACK Callback) {
	CAutoLock lock(&m_Lock);

	m_callback = Callback;

	return NOERROR;
}

//----------------------------------------------------------------------------
// inform the input pin of the allocator buffer we wish to use. See the
// input pin's SetDeliverBuffer method for comments.
//----------------------------------------------------------------------------

STDMETHODIMP CDXFilter::SetDeliveryBuffer(ALLOCATOR_PROPERTIES props, BYTE *m_pBuffer) {
	// have the input/output pins been created?
	if (!InputPin() || !OutputPin()) {
		return E_POINTER;
	}

	// they can't be connected if we're going to be changing delivery buffers
	//
	if (InputPin()->IsConnected() || OutputPin()->IsConnected()) {
		return E_INVALIDARG;
	}

	return ((CDXFilterInPin *)m_pInput)->SetDeliveryBuffer(props, m_pBuffer);
}

//----------------------------------------------------------------------------
// used to help speed input pin connection times. We return a partially
// specified media type - only the main type is specified. If we return
// anything BUT a major type, some codecs written improperly will crash
//----------------------------------------------------------------------------

HRESULT CDXFilterInPin::GetMediaType(int iPosition, CMediaType *pMediaType) {
	CheckPointer(pMediaType, E_POINTER);

	if (iPosition < 0) {
		return E_INVALIDARG;
	}
	if (iPosition > 0) {
		return VFW_S_NO_MORE_ITEMS;
	}

	*pMediaType = CMediaType();
	pMediaType->SetType(((CDXFilter *)m_pFilter)->m_mtAccept.Type());

	return S_OK;
}

//----------------------------------------------------------------------------
// override the CTransInPlaceInputPin's method, and return a new enumerator
// if the input pin is disconnected. This will allow GetMediaType to be
// called. If we didn't do this, EnumMediaTypes returns a failure code
// and GetMediaType is never called.
//----------------------------------------------------------------------------

STDMETHODIMP CDXFilterInPin::EnumMediaTypes(IEnumMediaTypes **ppEnum) {
	CheckPointer(ppEnum, E_POINTER);
	ValidateReadWritePtr(ppEnum, sizeof(IEnumMediaTypes *));

	// if the output pin isn't connected yet, offer the possibly
	// partially specified media type that has been set by the user

	if (!((CDXFilter *)m_pTIPFilter)->OutputPin()->IsConnected()) {
		// Create a new reference counted enumerator

		*ppEnum = new CEnumMediaTypes(this, NULL);

		return (*ppEnum) ? NOERROR : E_OUTOFMEMORY;
	}

	// if the output pin is connected, offer it's fully qualified media type

	return ((CDXFilter *)m_pTIPFilter)->OutputPin()->GetConnected()->EnumMediaTypes(ppEnum);
}

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------

STDMETHODIMP CDXFilterInPin::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) {
	if (m_pPrivateAllocator) {
		if (pAllocator != m_pPrivateAllocator) {
			return E_FAIL;
		} else {
#if !defined(_WIN32_WCE)
			// if the upstream guy wants to be read only and we don't, then that's bad
			// if the upstream guy doesn't request read only, but we do, that's okay
			if (bReadOnly && !DXFilter()->IsReadOnly()) {
				return E_FAIL;
			}
#endif
		}
	}

	return CTransInPlaceInputPin::NotifyAllocator(pAllocator, bReadOnly);
}

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------

STDMETHODIMP CDXFilterInPin::GetAllocator(IMemAllocator **ppAllocator) {
	if (m_pPrivateAllocator) {
		CheckPointer(ppAllocator, E_POINTER);

		*ppAllocator = m_pPrivateAllocator;
		m_pPrivateAllocator->AddRef();
		return NOERROR;
	} else {
		return CTransInPlaceInputPin::GetAllocator(ppAllocator);
	}
}

//----------------------------------------------------------------------------
// GetAllocatorRequirements: The upstream filter calls this to get our
// filter's allocator requirements. If the app has set the buffer, then
// we return those props. Otherwise, we use the default TransInPlace behavior.
//----------------------------------------------------------------------------

HRESULT CDXFilterInPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) {
	CheckPointer(pProps, E_POINTER);

	if (m_pPrivateAllocator) {
		*pProps = m_allocprops;
		return S_OK;
	} else {
		return CTransInPlaceInputPin::GetAllocatorRequirements(pProps);
	}
}

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------

HRESULT CDXFilterInPin::SetDeliveryBuffer(ALLOCATOR_PROPERTIES props, BYTE *pBuffer) {
	// don't allow more than one buffer

	if (props.cBuffers != 1) {
		return E_INVALIDARG;
	}
	if (!pBuffer) {
		return E_POINTER;
	}

	m_allocprops = props;
	m_pBuffer = pBuffer;

	// If there is an existing allocator, make sure that it is released
	// to prevent a memory leak
	if (m_pPrivateAllocator) {
		m_pPrivateAllocator->Release();
		m_pPrivateAllocator = NULL;
	}

	HRESULT hr = S_OK;

	m_pPrivateAllocator = new CDXFilterAllocator(this, &hr);
	if (!m_pPrivateAllocator) {
		return E_OUTOFMEMORY;
	}

	m_pPrivateAllocator->AddRef();
	return hr;
}

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------

HRESULT CDXFilterInPin::SetMediaType(const CMediaType *pmt) {
	m_bMediaTypeChanged = TRUE;

	return CTransInPlaceInputPin::SetMediaType(pmt);
}

//----------------------------------------------------------------------------
// don't allocate the memory, just use the buffer the app provided
//----------------------------------------------------------------------------

HRESULT CDXFilterAllocator::Alloc() {
	// look at the base class code to see where this came from!

	CAutoLock lck(this);

	// Check he has called SetProperties
	HRESULT hr = CBaseAllocator::Alloc();
	if (FAILED(hr)) {
		return hr;
	}

	// If the requirements haven't changed then don't reallocate
	if (hr == S_FALSE) {
		/* ASSERT(m_pBuffer); */
		return NOERROR;
	}
	/* ASSERT(hr == S_OK);  we use this fact in the loop below */

	// Free the old resources
	if (m_pBuffer) {
		ReallyFree();
	}

	// Compute the aligned size
	LONG lAlignedSize = m_lSize + m_lPrefix;
	if (m_lAlignment > 1) {
		LONG lRemainder = lAlignedSize % m_lAlignment;
		if (lRemainder != 0) {
			lAlignedSize += (m_lAlignment - lRemainder);
		}
	}

	// Create the contiguous memory block for the samples
	// making sure it's properly aligned (64K should be enough!)
	/* ASSERT(lAlignedSize % m_lAlignment == 0); */

	// don't create the buffer - use what was passed to us
	//
	m_pBuffer = m_pPin->m_pBuffer;

	if (m_pBuffer == NULL) {
		return E_OUTOFMEMORY;
	}

	LPBYTE pNext = m_pBuffer;
	CMediaSample *pSample;

	/* ASSERT(m_lAllocated == 0); */

	// Create the new samples - we have allocated m_lSize bytes for each sample
	// plus m_lPrefix bytes per sample as a prefix. We set the pointer to
	// the memory after the prefix - so that GetPointer() will return a pointer
	// to m_lSize bytes.
	for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {
		pSample = new CMediaSample(NAME("Sample Grabber memory media sample"), this, &hr,
		                           pNext + m_lPrefix, // GetPointer() value
		                           m_lSize);          // not including prefix

		/* ASSERT(SUCCEEDED(hr)); */
		if (pSample == NULL) return E_OUTOFMEMORY;

		// This CANNOT fail
		m_lFree.Add(pSample);
	}

	m_bChanged = FALSE;
	return NOERROR;
}

//----------------------------------------------------------------------------
// don't really free the memory
//----------------------------------------------------------------------------

void CDXFilterAllocator::ReallyFree() {
	// look at the base class code to see where this came from!

	// Should never be deleting this unless all buffers are freed

	/* ASSERT(m_lAllocated == m_lFree.GetCount()); */

	// Free up all the CMediaSamples

	CMediaSample *pSample;
	for (;;) {
		pSample = m_lFree.RemoveHead();
		if (pSample != NULL) {
			delete pSample;
		} else {
			break;
		}
	}

	m_lAllocated = 0;

	// don't free the buffer - let the app do it
}

//----------------------------------------------------------------------------
// SetProperties: Called by the upstream filter to set the allocator
// properties. The application has already allocated the buffer, so we reject
// anything that is not compatible with that, and return the actual props.
//----------------------------------------------------------------------------

HRESULT CDXFilterAllocator::SetProperties(ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual) {
	HRESULT hr = CMemAllocator::SetProperties(pRequest, pActual);

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

	ALLOCATOR_PROPERTIES *pRequired = &(m_pPin->m_allocprops);
	if (pRequest->cbAlign != pRequired->cbAlign) {
		return VFW_E_BADALIGN;
	}
	if (pRequest->cbPrefix != pRequired->cbPrefix) {
		return E_FAIL;
	}
	if (pRequest->cbBuffer > pRequired->cbBuffer) {
		return E_FAIL;
	}
	if (pRequest->cBuffers > pRequired->cBuffers) {
		return E_FAIL;
	}

	*pActual = *pRequired;

	m_lCount = pRequired->cBuffers;
	m_lSize = pRequired->cbBuffer;
	m_lAlignment = pRequired->cbAlign;
	m_lPrefix = pRequired->cbPrefix;

	return S_OK;
}

#endif
