#include "stdafx.h"
#include ".\deinterlacerfilter.h"
#include "..\..\DSUtil\MediaTypes.h"
#include "..\..\..\include\moreuuids.h"

CDeinterlacerFilter::CDeinterlacerFilter(LPUNKNOWN punk, HRESULT* phr)
	: CTransformFilter(NAME("CDeinterlacerFilter"), punk, __uuidof(CDeinterlacerFilter))
{
	if(phr) *phr = S_OK;
}

HRESULT CDeinterlacerFilter::CheckInputType(const CMediaType* mtIn)
{
	BITMAPINFOHEADER bih;
	if(!ExtractBIH(mtIn, &bih) /*|| bih.biHeight <= 0*/ || bih.biHeight <= 288)
		return E_FAIL;

	return mtIn->subtype == MEDIASUBTYPE_YUY2 || mtIn->subtype == MEDIASUBTYPE_UYVY
		|| mtIn->subtype == MEDIASUBTYPE_I420 || mtIn->subtype == MEDIASUBTYPE_YV12 || mtIn->subtype == MEDIASUBTYPE_IYUV 
		? S_OK 
		: E_FAIL;
}

HRESULT CDeinterlacerFilter::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut)
{
	return mtIn->subtype == mtOut->subtype ? S_OK : E_FAIL;
}

HRESULT CDeinterlacerFilter::Transform(IMediaSample* pIn, IMediaSample* pOut)
{
	HRESULT hr;

	AM_MEDIA_TYPE* pmt = NULL;
	if(SUCCEEDED(pOut->GetMediaType(&pmt)) && pmt)
	{
		CMediaType mt = *pmt;
		m_pOutput->SetMediaType(&mt);
		DeleteMediaType(pmt);
	}

	BYTE* pDataIn = NULL;
	if(FAILED(pIn->GetPointer(&pDataIn)) || !pDataIn)
		return S_FALSE;

	BYTE* pDataOut = NULL;
	if(FAILED(hr = pOut->GetPointer(&pDataOut)) || !pDataOut)
		return hr;

	const CMediaType& mtIn = m_pInput->CurrentMediaType();
	const CMediaType& mtOut = m_pOutput->CurrentMediaType();

	BITMAPINFOHEADER bihIn, bihOut;
	ExtractBIH(&mtIn, &bihIn);
	ExtractBIH(&mtOut, &bihOut);

	bool fInputFlipped = bihIn.biHeight >= 0 && bihIn.biCompression <= 3;
	bool fOutputFlipped = bihOut.biHeight >= 0 && bihOut.biCompression <= 3;
	bool fFlip = fInputFlipped != fOutputFlipped;

	int bppIn = !(bihIn.biBitCount&7) ? bihIn.biBitCount : 8;
	int bppOut = !(bihOut.biBitCount&7) ? bihOut.biBitCount : 8;
	int pitchIn = bihIn.biWidth*bppIn>>3;
	int pitchOut = bihOut.biWidth*bppOut>>3;
	BYTE* pDataOut2 = pDataOut;
	if(fFlip) {pDataOut2 += pitchOut*(bihIn.biHeight-1); pitchOut = -pitchOut;}

	if(mtIn.subtype == MEDIASUBTYPE_YUY2 || mtIn.subtype == MEDIASUBTYPE_UYVY)
	{
		DeinterlaceBlend(pDataOut, pDataIn, pitchIn, bihIn.biHeight, pitchOut, pitchIn);
	}
	else if(mtIn.subtype == MEDIASUBTYPE_I420 || mtIn.subtype == MEDIASUBTYPE_YV12 || mtIn.subtype == MEDIASUBTYPE_IYUV)
	{
		DeinterlaceBlend(pDataOut, pDataIn, pitchIn, bihIn.biHeight, pitchOut, pitchIn);

		int sizeIn = bihIn.biHeight*pitchIn, sizeOut = abs(bihOut.biHeight)*pitchOut;
		pitchIn /= 2; pitchOut /= 2;
		bihIn.biHeight /= 2;
		pDataIn += sizeIn; pDataOut += sizeOut;
		DeinterlaceBlend(pDataOut, pDataIn, pitchIn, bihIn.biHeight, pitchOut, pitchIn);

		pDataIn += sizeIn/4; pDataOut += sizeOut/4;
		DeinterlaceBlend(pDataOut, pDataIn, pitchIn, bihIn.biHeight, pitchOut, pitchIn);
	}

	return S_OK;
}

HRESULT CDeinterlacerFilter::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties)
{
	if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED;

	BITMAPINFOHEADER bih;
	ExtractBIH(&m_pOutput->CurrentMediaType(), &bih);

	pProperties->cBuffers = 1;
	pProperties->cbBuffer = bih.biSizeImage;
	pProperties->cbAlign = 1;
	pProperties->cbPrefix = 0;

	HRESULT hr;
	ALLOCATOR_PROPERTIES Actual;
    if(FAILED(hr = pAllocator->SetProperties(pProperties, &Actual))) 
		return hr;

    return pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer
		? E_FAIL
		: NOERROR;
}

HRESULT CDeinterlacerFilter::GetMediaType(int iPosition, CMediaType* pmt)
{
    if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED;
	if(iPosition < 0) return E_INVALIDARG;
	if(iPosition > 0) return VFW_S_NO_MORE_ITEMS;
	*pmt = m_pInput->CurrentMediaType();
	CorrectMediaType(pmt);
	return S_OK;
}