/*
 * (C) 2003-2006 Gabest
 * (C) 2006-2016 see Authors.txt
 *
 * This file is part of MPC-BE.
 *
 * MPC-BE is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * MPC-BE 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "stdafx.h"
#include <algorithm>
#include <MMReg.h>
#include "MatroskaMuxer.h"
#include "../../../DSUtil/DSUtil.h"

#ifdef REGISTER_FILTER
#include <InitGuid.h>
#endif
#include <moreuuids.h>

using namespace MatroskaWriter;

#ifdef REGISTER_FILTER

const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] = {
	{&MEDIATYPE_Stream, &MEDIASUBTYPE_Matroska}
};

const AMOVIESETUP_PIN sudpPins[] = {
	{L"Input", FALSE, FALSE, FALSE, TRUE, &CLSID_NULL, NULL, 0, NULL},
	{L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, _countof(sudPinTypesOut), sudPinTypesOut}
};

const AMOVIESETUP_FILTER sudFilter[] = {
	{&__uuidof(CMatroskaMuxerFilter), MatroskaMuxerName, MERIT_DO_NOT_USE, _countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory}
};

CFactoryTemplate g_Templates[] = {
	{sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CMatroskaMuxerFilter>, NULL, &sudFilter[0]}
};

int g_cTemplates = _countof(g_Templates);

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

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

#include "../../filters/Filters.h"

CFilterApp theApp;

#endif

//
// CMatroskaMuxerFilter
//

CMatroskaMuxerFilter::CMatroskaMuxerFilter(LPUNKNOWN pUnk, HRESULT* phr)
	: CBaseFilter(NAME("CMatroskaMuxerFilter"), pUnk, this, __uuidof(this))
	, m_rtCurrent(0)
	, m_fNegative(true)
	, m_fPositive(false)
{
	if (phr) {
		*phr = S_OK;
	}

	m_pOutput.Attach(DNew CMatroskaMuxerOutputPin(NAME("CMatroskaMuxerOutputPin"), this, this, phr));

	AddInput();

	srand(clock());
}

CMatroskaMuxerFilter::~CMatroskaMuxerFilter()
{
	CAutoLock cAutoLock(this);
}

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

	*ppv = NULL;

	return
		//QI(IAMFilterMiscFlags)
		QI(IMediaSeeking)
		QI(IMatroskaMuxer)
		__super::NonDelegatingQueryInterface(riid, ppv);
}

UINT CMatroskaMuxerFilter::GetTrackNumber(const CBasePin* pPin)
{
	UINT nTrackNumber = 0;

	POSITION pos = m_pInputs.GetHeadPosition();
	while (pos) {
		nTrackNumber++;
		if (m_pInputs.GetNext(pos) == pPin) {
			return nTrackNumber;
		}
	}

	return 0;
}

void CMatroskaMuxerFilter::AddInput()
{
	POSITION pos = m_pInputs.GetHeadPosition();
	while (pos) {
		CBasePin* pPin = m_pInputs.GetNext(pos);
		if (!pPin->IsConnected()) {
			return;
		}
	}

	CStringW name;
	name.Format(L"Track %u", m_pInputs.GetCount() + 1);

	HRESULT hr;
	CAutoPtr<CMatroskaMuxerInputPin> pPin(DNew CMatroskaMuxerInputPin(name, this, this, &hr));
	m_pInputs.AddTail(pPin);
}

int CMatroskaMuxerFilter::GetPinCount()
{
	return (int)m_pInputs.GetCount() + (m_pOutput ? 1 : 0);
}

CBasePin* CMatroskaMuxerFilter::GetPin(int n)
{
	CAutoLock cAutoLock(this);

	if (n >= 0 && n < (int)m_pInputs.GetCount()) {
		if (POSITION pos = m_pInputs.FindIndex(n)) {
			return m_pInputs.GetAt(pos);
		}
	}

	if (n == (int)m_pInputs.GetCount() && m_pOutput) {
		return m_pOutput;
	}

	return NULL;
}

STDMETHODIMP CMatroskaMuxerFilter::Stop()
{
	CAutoLock cAutoLock(this);

	HRESULT hr;

	if (FAILED(hr = __super::Stop())) {
		return hr;
	}

	CallWorker(CMD_EXIT);

	return hr;
}

STDMETHODIMP CMatroskaMuxerFilter::Pause()
{
	CAutoLock cAutoLock(this);

	FILTER_STATE fs = m_State;

	HRESULT hr;

	if (FAILED(hr = __super::Pause())) {
		return hr;
	}

	if (fs == State_Stopped && m_pOutput) {
		CAMThread::Create();
		CallWorker(CMD_RUN);
	}

	return hr;
}

STDMETHODIMP CMatroskaMuxerFilter::Run(REFERENCE_TIME tStart)
{
	CAutoLock cAutoLock(this);

	HRESULT hr;

	if (FAILED(hr = __super::Run(tStart))) {
		return hr;
	}

	return hr;
}

// IAMFilterMiscFlags

STDMETHODIMP_(ULONG) CMatroskaMuxerFilter::GetMiscFlags()
{
	return AM_FILTER_MISC_FLAGS_IS_RENDERER;
}

// IMediaSeeking

STDMETHODIMP CMatroskaMuxerFilter::GetCapabilities(DWORD* pCapabilities)
{
	CheckPointer(pCapabilities, E_POINTER);

	*pCapabilities = AM_SEEKING_CanGetDuration | AM_SEEKING_CanGetCurrentPos;

	return S_OK;
}

STDMETHODIMP CMatroskaMuxerFilter::CheckCapabilities(DWORD* pCapabilities)
{
	CheckPointer(pCapabilities, E_POINTER);
	if (*pCapabilities == 0) {
		return S_OK;
	}
	DWORD caps;
	GetCapabilities(&caps);
	caps &= *pCapabilities;
	return caps == 0 ? E_FAIL : caps == *pCapabilities ? S_OK : S_FALSE;
}

STDMETHODIMP CMatroskaMuxerFilter::IsFormatSupported(const GUID* pFormat)
{
	return !pFormat ? E_POINTER : *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}

STDMETHODIMP CMatroskaMuxerFilter::QueryPreferredFormat(GUID* pFormat)
{
	return GetTimeFormat(pFormat);
}

STDMETHODIMP CMatroskaMuxerFilter::GetTimeFormat(GUID* pFormat)
{
	CheckPointer(pFormat, E_POINTER);

	*pFormat = TIME_FORMAT_MEDIA_TIME;

	return S_OK;
}

STDMETHODIMP CMatroskaMuxerFilter::IsUsingTimeFormat(const GUID* pFormat)
{
	return IsFormatSupported(pFormat);
}

STDMETHODIMP CMatroskaMuxerFilter::SetTimeFormat(const GUID* pFormat)
{
	return S_OK == IsFormatSupported(pFormat) ? S_OK : E_INVALIDARG;
}

STDMETHODIMP CMatroskaMuxerFilter::GetDuration(LONGLONG* pDuration)
{
	CheckPointer(pDuration, E_POINTER);
	*pDuration = 0;
	POSITION pos = m_pInputs.GetHeadPosition();
	while (pos) {
		REFERENCE_TIME rt = m_pInputs.GetNext(pos)->m_rtDur;
		if (rt > *pDuration) {
			*pDuration = rt;
		}
	}
	return S_OK;
}

STDMETHODIMP CMatroskaMuxerFilter::GetStopPosition(LONGLONG* pStop)
{
	return E_NOTIMPL;
}

STDMETHODIMP CMatroskaMuxerFilter::GetCurrentPosition(LONGLONG* pCurrent)
{
	CheckPointer(pCurrent, E_POINTER);
	*pCurrent = m_rtCurrent;
	return S_OK;
}

STDMETHODIMP CMatroskaMuxerFilter::ConvertTimeFormat(LONGLONG* pTarget, const GUID* pTargetFormat, LONGLONG Source, const GUID* pSourceFormat)
{
	return E_NOTIMPL;
}

STDMETHODIMP CMatroskaMuxerFilter::SetPositions(LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags)
{
	return E_NOTIMPL;
}

STDMETHODIMP CMatroskaMuxerFilter::GetPositions(LONGLONG* pCurrent, LONGLONG* pStop)
{
	return E_NOTIMPL;
}

STDMETHODIMP CMatroskaMuxerFilter::GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest)
{
	return E_NOTIMPL;
}

STDMETHODIMP CMatroskaMuxerFilter::SetRate(double dRate)
{
	return E_NOTIMPL;
}

STDMETHODIMP CMatroskaMuxerFilter::GetRate(double* pdRate)
{
	return E_NOTIMPL;
}

STDMETHODIMP CMatroskaMuxerFilter::GetPreroll(LONGLONG* pllPreroll)
{
	return E_NOTIMPL;
}

// IMatroskaMuxer

STDMETHODIMP CMatroskaMuxerFilter::CorrectTimeOffset(bool fNegative, bool fPositive)
{
	m_fNegative = fNegative;
	m_fPositive = fPositive;
	return S_OK;
}

//

ULONGLONG GetStreamPosition(IStream* pStream)
{
	ULARGE_INTEGER pos = {0, 0};
	pStream->Seek(*(LARGE_INTEGER*)&pos, STREAM_SEEK_CUR, &pos);
	return pos.QuadPart;
}

ULONGLONG SetStreamPosition(IStream* pStream, ULONGLONG seekpos)
{
	LARGE_INTEGER pos;
	pos.QuadPart = seekpos;
	ULARGE_INTEGER posnew;
	posnew.QuadPart = GetStreamPosition(pStream);
	pStream->Seek(pos, STREAM_SEEK_SET, &posnew);
	return posnew.QuadPart;
}

#pragma warning(push)
#pragma warning(disable: 4702)
DWORD CMatroskaMuxerFilter::ThreadProc()
{
	CComQIPtr<IStream> pStream;

	if (!m_pOutput || !(pStream = m_pOutput->GetConnected())) {
		for (;;) {
			DWORD cmd = GetRequest();
			if (cmd == CMD_EXIT) {
				CAMThread::m_hThread = NULL;
			}
			Reply(S_OK);
			if (cmd == CMD_EXIT) {
				return 0;
			}
		}
	}

	REFERENCE_TIME rtDur = 0;
	GetDuration(&rtDur);

	SetStreamPosition(pStream, 0);

	ULARGE_INTEGER uli = {0};
	pStream->SetSize(uli);

	EBML hdr;
	hdr.DocType.Set("matroska");
	hdr.DocTypeVersion.Set(1);
	hdr.DocTypeReadVersion.Set(1);
	hdr.Write(pStream);

	Segment().Write(pStream);
	ULONGLONG segpos = GetStreamPosition(pStream);

	// TODO
	QWORD voidlen = 100;
	if (rtDur > 0) {
		voidlen += QWORD(1.0 * rtDur / MAXCLUSTERTIME / 10000 + 0.5) * 20;
	} else {
		voidlen += QWORD(1.0 * 1000 * 60 * 60 * 24 / MAXCLUSTERTIME + 0.5) * 20; // when no duration is known, allocate for 24 hours (~340k)
	}
	ULONGLONG voidpos = GetStreamPosition(pStream);
	{
		Void v(voidlen);
		voidlen = v.Size();
		v.Write(pStream);
	}

	// Meta Seek

	Seek seek;
	CAutoPtr<SeekHead> sh;

	// Segment Info

	sh.Attach(DNew SeekHead());
	sh->ID.Set(0x1549A966);
	sh->Position.Set(GetStreamPosition(pStream) - segpos);
	seek.SeekHeads.AddTail(sh);

	ULONGLONG infopos = GetStreamPosition(pStream);
	Info info;
	info.MuxingApp.Set(L"DirectShow Matroska Muxer");
	info.TimeCodeScale.Set(1000000);
	info.Duration.Set((float)rtDur / 10000);
	struct tm _2001 = {0, 0, 0, 1, 0, 101, 0, 0, 1};
	info.DateUTC.Set((_time64(NULL) - _mktime64(&_2001)) * 1000000000);
	info.Write(pStream);

	// Tracks

	sh.Attach(DNew SeekHead());
	sh->ID.Set(0x1654AE6B);
	sh->Position.Set(GetStreamPosition(pStream) - segpos);
	seek.SeekHeads.AddTail(sh);

	UINT64 TrackNumber = 0;
	/*
		CNode<Track> Tracks;
		CAutoPtr<Track> pT(DNew Track());
		POSITION pos = m_pInputs.GetHeadPosition();
		for (int i = 1; pos; i++)
		{
			CMatroskaMuxerInputPin* pPin = m_pInputs.GetNext(pos);
			if (!pPin->IsConnected()) continue;

			CAutoPtr<TrackEntry> pTE(DNew TrackEntry());
			*pTE = *pPin->GetTrackEntry();
			if (TrackNumber == 0 && pTE->TrackType == TrackEntry::TypeVideo)
				TrackNumber = pTE->TrackNumber;
			pT->TrackEntries.AddTail(pTE);
		}
		Tracks.AddTail(pT);
		Tracks.Write(pStream);

		if (TrackNumber == 0) TrackNumber = 1;
	*/
	bool fTracksWritten = false;

	//

	Cluster c;
	c.TimeCode.Set(0);

	bool fFirstBlock = true;
	INT64 firstTimeCode = 0;

	CAtlList<CMatroskaMuxerInputPin*> pActivePins;

	POSITION pos = m_pInputs.GetHeadPosition();
	while (pos) {
		CMatroskaMuxerInputPin* pPin = m_pInputs.GetNext(pos);
		if (pPin->IsConnected()) {
			pActivePins.AddTail(pPin);
		}
	}

	for (;;) {
		DWORD cmd = GetRequest();

		switch (cmd) {
			default:
			case CMD_EXIT:
				CAMThread::m_hThread = NULL;
				Reply(S_OK);
				return 0;

			case CMD_RUN:
				Reply(S_OK);

				Cue cue;
				ULONGLONG lastcueclusterpos = (ULONGLONG) - 1;
				INT64 lastcuetimecode = (INT64) - 1;
				UINT64 nBlocksInCueTrack = 0;

				while (!CheckRequest(NULL)) {
					if (m_State == State_Paused) {
						Sleep(10);
						continue;
					}

					int nPinsGotSomething = 0, nPinsNeeded = 0;
					CMatroskaMuxerInputPin* pPin = NULL;
					REFERENCE_TIME rtMin = _I64_MAX;

					pos = pActivePins.GetHeadPosition();
					while (pos) {
						CMatroskaMuxerInputPin* pTmp = pActivePins.GetNext(pos);

						CAutoLock cAutoLock(&pTmp->m_csQueue);

						if (pTmp->m_blocks.IsEmpty() && pTmp->m_fEndOfStreamReceived) {
							pActivePins.RemoveAt(pActivePins.Find(pTmp));
							continue;
						}

						if (pTmp->GetTrackEntry()->TrackType != TrackEntry::TypeSubtitle) {
							nPinsNeeded++;
						}

						if (!pTmp->m_blocks.IsEmpty()) {
							if (pTmp->GetTrackEntry()->TrackType != TrackEntry::TypeSubtitle) {
								nPinsGotSomething++;
							}

							if (!pTmp->m_blocks.IsEmpty()) {
								REFERENCE_TIME rt = pTmp->m_blocks.GetHead()->Block.TimeCode;
								if (rt < rtMin) {
									rtMin = rt;
									pPin = pTmp;
								}
							}
						}
					}

					if (pActivePins.IsEmpty()) {
						break;
					}

					if (!pPin || nPinsNeeded > nPinsGotSomething || !pPin && nPinsGotSomething == 0) {
						Sleep(1);
						continue;
					}

					if (!fTracksWritten) {
						CNode<Track> Tracks;
						CAutoPtr<Track> pT(DNew Track());
						pos = pActivePins.GetHeadPosition();
						for (int i = 1; pos; i++) {
							CMatroskaMuxerInputPin* pActivePin = pActivePins.GetNext(pos);

							CAutoPtr<TrackEntry> pTE(DNew TrackEntry());
							*pTE = *pActivePin->GetTrackEntry();
							if (TrackNumber == 0 && pTE->TrackType == TrackEntry::TypeVideo) {
								TrackNumber = pTE->TrackNumber;
							}
							pT->TrackEntries.AddTail(pTE);
						}
						Tracks.AddTail(pT);
						Tracks.Write(pStream);

						if (TrackNumber == 0) {
							TrackNumber = 1;
						}

						fTracksWritten = true;
					}

					ASSERT(pPin);

					CAutoPtr<BlockGroup> b;

					{
						CAutoLock cAutoLock(&pPin->m_csQueue);
						b.Attach(pPin->m_blocks.RemoveHead().Detach());
					}

					if (b) {
						if (fFirstBlock) {
							if (b->Block.TimeCode < 0 && m_fNegative || b->Block.TimeCode > 0 && m_fPositive) {
								firstTimeCode = b->Block.TimeCode;
							}
							fFirstBlock = false;
						}

						b->Block.TimeCode -= firstTimeCode;
						b->Block.TimeCodeStop -= firstTimeCode;

						/*
						TRACE(_T("Muxing (%d): %I64d-%I64d dur=%I64d (c=%d, co=%dms), cnt=%d, ref=%d\n"),
							GetTrackNumber(pPin),
							(INT64)b->Block.TimeCode, (INT64)b->Block.TimeCodeStop, (UINT64)b->BlockDuration,
							(int)((b->Block.TimeCode)/MAXCLUSTERTIME), (int)(b->Block.TimeCode%MAXCLUSTERTIME),
							b->Block.BlockData.GetCount(), (int)b->ReferenceBlock);
						*/
						if (b->Block.TimeCode < SHRT_MIN /*0*/) {
							ASSERT(0);
							continue;
						}

						while ((INT64)(c.TimeCode + MAXCLUSTERTIME) < b->Block.TimeCode) {
							if (!c.BlockGroups.IsEmpty()) {
								sh.Attach(DNew SeekHead());
								sh->ID.Set(c.GetID()/*0x1F43B675*/);
								sh->Position.Set(GetStreamPosition(pStream) - segpos);
								seek.SeekHeads.AddTail(sh);

								c.Write(pStream); // TODO: write blocks
							}

							c.TimeCode.Set(c.TimeCode + MAXCLUSTERTIME);
							c.BlockGroups.RemoveAll();
							nBlocksInCueTrack = 0;
						}

						if (b->Block.TrackNumber == TrackNumber) {
							nBlocksInCueTrack++;
						}

						if (b->ReferenceBlock == 0 && b->Block.TrackNumber == TrackNumber) {
							ULONGLONG clusterpos = GetStreamPosition(pStream) - segpos;
							if (lastcueclusterpos != clusterpos || lastcuetimecode + 1000 < b->Block.TimeCode) {
								CAutoPtr<CueTrackPosition> ctp(DNew CueTrackPosition());
								ctp->CueTrack.Set(b->Block.TrackNumber);
								ctp->CueClusterPosition.Set(clusterpos);
								if (!c.BlockGroups.IsEmpty()) {
									ctp->CueBlockNumber.Set(nBlocksInCueTrack);
								}
								CAutoPtr<CuePoint> cp(DNew CuePoint());
								cp->CueTime.Set(b->Block.TimeCode);
								cp->CueTrackPositions.AddTail(ctp);
								cue.CuePoints.AddTail(cp);
								lastcueclusterpos = clusterpos;
								lastcuetimecode = b->Block.TimeCode;
							}
						}

						info.Duration.Set(std::max<float>(info.Duration, (float)b->Block.TimeCodeStop));

						m_rtCurrent = b->Block.TimeCode * 10000;

						b->Block.TimeCode -= c.TimeCode;
						c.BlockGroups.AddTail(b);
					}
				}

				if (!c.BlockGroups.IsEmpty()) {
					sh.Attach(DNew SeekHead());
					sh->ID.Set(c.GetID()/*0x1F43B675*/);
					sh->Position.Set(GetStreamPosition(pStream) - segpos);
					seek.SeekHeads.AddTail(sh);

					c.Write(pStream);
				}

				if (!cue.CuePoints.IsEmpty()) {
					sh.Attach(DNew SeekHead());
					sh->ID.Set(cue.GetID()/*0x1C53BB6B*/);
					sh->Position.Set(GetStreamPosition(pStream) - segpos);
					seek.SeekHeads.AddTail(sh);

					cue.Write(pStream);
				}

				{
					Tags tags;

					sh.Attach(DNew SeekHead());
					sh->ID.Set(tags.GetID());
					sh->Position.Set(GetStreamPosition(pStream) - segpos);
					seek.SeekHeads.AddTail(sh);

					tags.Write(pStream);
				}

				SetStreamPosition(pStream, voidpos);
				QWORD len = voidlen - seek.Size();
				ASSERT(len >= 0 && len != 1);
				seek.Write(pStream);

				if (len == 0) {
					// nothing to do
				} else if (len >= 2) {
					for (QWORD i = 0; i < 8; i++) {
						if (len >= (QWORD(1) << i * 7) - 2 && len <= (QWORD(1) << (i + 1) * 7) - 2) {
							Void(len - 2 - i).Write(pStream);
							break;
						}
					}
				}

				if (abs(m_rtCurrent - (REFERENCE_TIME)info.Duration * 10000) > 10000000i64) {
					info.Duration.Set((float)m_rtCurrent / 10000 + 1);
				}

				SetStreamPosition(pStream, infopos);
				info.Write(pStream);

				// TODO: write some tags

				m_pOutput->DeliverEndOfStream();

				break;
		}
	}

	ASSERT(0); // we should only exit via CMD_EXIT
#pragma warning(pop)
}

//
// CMatroskaMuxerInputPin
//

CMatroskaMuxerInputPin::CMatroskaMuxerInputPin(LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
	: CBaseInputPin(NAME("CMatroskaMuxerInputPin"), pFilter, pLock, phr, pName)
	, m_fActive(false)
	, m_rtLastStart(0)
	, m_rtLastStop(0)
	, m_rtDur(0)
	, m_fEndOfStreamReceived(false)
{
}

CMatroskaMuxerInputPin::~CMatroskaMuxerInputPin()
{
}

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

	return
		__super::NonDelegatingQueryInterface(riid, ppv);
}

HRESULT CMatroskaMuxerInputPin::CheckMediaType(const CMediaType* pmt)
{
	return pmt->majortype == MEDIATYPE_Video && (pmt->formattype == FORMAT_VideoInfo
												 || pmt->formattype == FORMAT_VideoInfo2)
		   //|| pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_MPEG1Payload && pmt->formattype == FORMAT_MPEGVideo
		   //|| pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_MPEG2_VIDEO && pmt->formattype == FORMAT_MPEG2_VIDEO
		   || pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_DiracVideo && pmt->formattype == FORMAT_DiracVideoInfo
		   || pmt->majortype == MEDIATYPE_Audio && pmt->formattype == FORMAT_WaveFormatEx && pmt->subtype == FOURCCMap(((WAVEFORMATEX*)pmt->pbFormat)->wFormatTag)
		   || pmt->majortype == MEDIATYPE_Audio && pmt->subtype == MEDIASUBTYPE_Vorbis && pmt->formattype == FORMAT_VorbisFormat
		   || pmt->majortype == MEDIATYPE_Audio && pmt->subtype == MEDIASUBTYPE_Vorbis2 && pmt->formattype == FORMAT_VorbisFormat2
		   || pmt->majortype == MEDIATYPE_Audio && (pmt->subtype == MEDIASUBTYPE_14_4
													|| pmt->subtype == MEDIASUBTYPE_28_8
													|| pmt->subtype == MEDIASUBTYPE_ATRC
													|| pmt->subtype == MEDIASUBTYPE_COOK
													|| pmt->subtype == MEDIASUBTYPE_DNET
													|| pmt->subtype == MEDIASUBTYPE_SIPR) && pmt->formattype == FORMAT_WaveFormatEx
		   || pmt->majortype == MEDIATYPE_Text && pmt->subtype == MEDIASUBTYPE_NULL && pmt->formattype == FORMAT_None
		   || pmt->majortype == MEDIATYPE_Subtitle && pmt->formattype == FORMAT_SubtitleInfo
		   ? S_OK
		   : E_INVALIDARG;
}

HRESULT CMatroskaMuxerInputPin::BreakConnect()
{
	HRESULT hr;

	if (FAILED(hr = __super::BreakConnect())) {
		return hr;
	}

	m_pTE.Free();

	return hr;
}

HRESULT CMatroskaMuxerInputPin::CompleteConnect(IPin* pPin)
{
	HRESULT hr;

	if (FAILED(hr = __super::CompleteConnect(pPin))) {
		return hr;
	}

	m_rtDur = 0;
	CComQIPtr<IMediaSeeking> pMS;
	if ((pMS = GetFilterFromPin(pPin)) || (pMS = pPin)) {
		pMS->GetDuration(&m_rtDur);
	}

	m_pTE.Free();
	m_pTE.Attach(DNew TrackEntry());

	m_pTE->TrackUID.Set(rand());
	m_pTE->MinCache.Set(1);
	m_pTE->MaxCache.Set(1);
	m_pTE->TrackNumber.Set((static_cast<CMatroskaMuxerFilter*>(m_pFilter))->GetTrackNumber(this));

	hr = E_FAIL;

	if (m_mt.majortype == MEDIATYPE_Video) {
		m_pTE->TrackType.Set(TrackEntry::TypeVideo);

		if (m_mt.formattype == FORMAT_VideoInfo
				&& m_mt.subtype == MEDIASUBTYPE_RV10 || m_mt.subtype == MEDIASUBTYPE_RV20
				|| m_mt.subtype == MEDIASUBTYPE_RV30 || m_mt.subtype == MEDIASUBTYPE_RV40) {
			m_pTE->CodecID.Set("V_REAL/RV00");
			m_pTE->CodecID.SetAt(9, (BYTE)(m_mt.subtype.Data1 >> 16));

			if (m_mt.formattype == FORMAT_VideoInfo) {
				VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)m_mt.pbFormat;
				if (m_mt.cbFormat > sizeof(VIDEOINFOHEADER)) {
					m_pTE->CodecPrivate.SetCount(m_mt.cbFormat - sizeof(VIDEOINFOHEADER));
					memcpy(m_pTE->CodecPrivate, m_mt.pbFormat + sizeof(VIDEOINFOHEADER), m_pTE->CodecPrivate.GetCount());
				}
				m_pTE->DefaultDuration.Set(vih->AvgTimePerFrame * 100);
				m_pTE->DescType = TrackEntry::DescVideo;
				m_pTE->v.PixelWidth.Set(vih->bmiHeader.biWidth);
				m_pTE->v.PixelHeight.Set(abs(vih->bmiHeader.biHeight));
				if (vih->AvgTimePerFrame > 0) {
					m_pTE->v.FramePerSec.Set((float)(10000000.0 / vih->AvgTimePerFrame));
				}
			} else if (m_mt.formattype == FORMAT_VideoInfo2) {
				VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)m_mt.pbFormat;
				if (m_mt.cbFormat > sizeof(VIDEOINFOHEADER2)) {
					m_pTE->CodecPrivate.SetCount(m_mt.cbFormat - sizeof(VIDEOINFOHEADER2));
					memcpy(m_pTE->CodecPrivate, m_mt.pbFormat + sizeof(VIDEOINFOHEADER2), m_pTE->CodecPrivate.GetCount());
				}
				m_pTE->DefaultDuration.Set(vih->AvgTimePerFrame * 100);
				m_pTE->DescType = TrackEntry::DescVideo;
				m_pTE->v.PixelWidth.Set(vih->bmiHeader.biWidth);
				m_pTE->v.PixelHeight.Set(abs(vih->bmiHeader.biHeight));
				if (vih->AvgTimePerFrame > 0) {
					m_pTE->v.FramePerSec.Set((float)(10000000.0 / vih->AvgTimePerFrame));
				}
				m_pTE->v.DisplayWidth.Set(vih->dwPictAspectRatioX);
				m_pTE->v.DisplayHeight.Set(vih->dwPictAspectRatioY);
			} else {
				ASSERT(0);
				return hr;
			}

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_VideoInfo) {
			m_pTE->CodecID.Set("V_MS/VFW/FOURCC");

			VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)m_mt.pbFormat;
			m_pTE->CodecPrivate.SetCount(m_mt.cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader));
			memcpy(m_pTE->CodecPrivate, &vih->bmiHeader, m_pTE->CodecPrivate.GetCount());
			m_pTE->DefaultDuration.Set(vih->AvgTimePerFrame * 100);
			m_pTE->DescType = TrackEntry::DescVideo;
			m_pTE->v.PixelWidth.Set(vih->bmiHeader.biWidth);
			m_pTE->v.PixelHeight.Set(abs(vih->bmiHeader.biHeight));
			if (vih->AvgTimePerFrame > 0) {
				m_pTE->v.FramePerSec.Set((float)(10000000.0 / vih->AvgTimePerFrame));
			}

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_VideoInfo2) {
			m_pTE->CodecID.Set("V_MS/VFW/FOURCC");

			VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)m_mt.pbFormat;
			m_pTE->CodecPrivate.SetCount(m_mt.cbFormat - FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader));
			memcpy(m_pTE->CodecPrivate, &vih->bmiHeader, m_pTE->CodecPrivate.GetCount());
			m_pTE->DefaultDuration.Set(vih->AvgTimePerFrame * 100);
			m_pTE->DescType = TrackEntry::DescVideo;
			m_pTE->v.PixelWidth.Set(vih->bmiHeader.biWidth);
			m_pTE->v.PixelHeight.Set(abs(vih->bmiHeader.biHeight));
			m_pTE->v.DisplayWidth.Set(vih->dwPictAspectRatioX);
			m_pTE->v.DisplayHeight.Set(vih->dwPictAspectRatioY);
			if (vih->AvgTimePerFrame > 0) {
				m_pTE->v.FramePerSec.Set((float)(10000000.0 / vih->AvgTimePerFrame));
			}

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_DiracVideoInfo) {
			m_pTE->CodecID.Set("V_DIRAC");

			DIRACINFOHEADER* vih = (DIRACINFOHEADER*)m_mt.pbFormat;
			m_pTE->CodecPrivate.SetCount(vih->cbSequenceHeader);
			memcpy(m_pTE->CodecPrivate, (BYTE*)&vih->dwSequenceHeader[0], m_pTE->CodecPrivate.GetCount());
			m_pTE->DefaultDuration.Set(vih->hdr.AvgTimePerFrame * 100);
			m_pTE->DescType = TrackEntry::DescVideo;
			m_pTE->v.PixelWidth.Set(vih->hdr.bmiHeader.biWidth);
			m_pTE->v.PixelHeight.Set(abs(vih->hdr.bmiHeader.biHeight));
			// m_pTE->v.DisplayWidth.Set(vih->dwPictAspectRatioX);
			// m_pTE->v.DisplayHeight.Set(vih->dwPictAspectRatioY);
			if (vih->hdr.AvgTimePerFrame > 0) {
				m_pTE->v.FramePerSec.Set((float)(10000000.0 / vih->hdr.AvgTimePerFrame));
			}

			hr = S_OK;
		}
		/*
				else if (m_mt.formattype == FORMAT_MPEGVideo)
				{
					m_pTE->CodecID.Set("V_DSHOW/MPEG1VIDEO"); // V_MPEG1

					MPEG1VIDEOINFO* pm1vi = (MPEG1VIDEOINFO*)m_mt.pbFormat;
					m_pTE->CodecPrivate.SetCount(m_mt.FormatLength());
					memcpy(m_pTE->CodecPrivate, m_mt.pbFormat, m_pTE->CodecPrivate.GetCount());
					m_pTE->DefaultDuration.Set(pm1vi->hdr.AvgTimePerFrame*100);
					m_pTE->DescType = TrackEntry::DescVideo;
					m_pTE->v.PixelWidth.Set(pm1vi->hdr.bmiHeader.biWidth);
					m_pTE->v.PixelHeight.Set(abs(pm1vi->hdr.bmiHeader.biHeight));
					if (pm1vi->hdr.AvgTimePerFrame > 0)
						m_pTE->v.FramePerSec.Set((float)(10000000.0 / pm1vi->hdr.AvgTimePerFrame));

					hr = S_OK;
				}
				else if (m_mt.formattype == FORMAT_MPEG2_VIDEO)
				{
					m_pTE->CodecID.Set("V_DSHOW/MPEG2VIDEO"); // V_MPEG2

					MPEG2VIDEOINFO* pm2vi = (MPEG2VIDEOINFO*)m_mt.pbFormat;
					m_pTE->CodecPrivate.SetCount(m_mt.FormatLength());
					memcpy(m_pTE->CodecPrivate, m_mt.pbFormat, m_pTE->CodecPrivate.GetCount());
					m_pTE->DefaultDuration.Set(pm2vi->hdr.AvgTimePerFrame*100);
					m_pTE->DescType = TrackEntry::DescVideo;
					m_pTE->v.PixelWidth.Set(pm2vi->hdr.bmiHeader.biWidth);
					m_pTE->v.PixelHeight.Set(abs(pm2vi->hdr.bmiHeader.biHeight));
					if (pm2vi->hdr.AvgTimePerFrame > 0)
						m_pTE->v.FramePerSec.Set((float)(10000000.0 / pm2vi->hdr.AvgTimePerFrame));

					hr = S_OK;
				}
		*/
	} else if (m_mt.majortype == MEDIATYPE_Audio) {
		m_pTE->TrackType.Set(TrackEntry::TypeAudio);

		if (m_mt.formattype == FORMAT_WaveFormatEx
				&& ((WAVEFORMATEX*)m_mt.pbFormat)->wFormatTag == WAVE_FORMAT_RAW_AAC1
				&& m_mt.cbFormat >= sizeof(WAVEFORMATEX) + 2) {
			WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
			BYTE* p = (BYTE*)(wfe + 1);

			DWORD nSamplesPerSec = wfe->nSamplesPerSec;

			int profile = (p[0] >> 3) - 1;
			int rate1 = ((p[0] & 7) << 1) | (p[1] >> 7);
			int channels = ((p[1] >> 3) & 15);
			int rate2 = rate1;

			if (wfe->cbSize >= 5) {
				profile = 4;

				int exttype = (p[2] << 3) | (p[3] >> 5);
				ASSERT(exttype == 0x2B7);
				ASSERT((p[3] & 31) == 5);
				ASSERT((p[4] >> 7) == 1);
				rate2 = ((p[4] >> 3) & 15);

				if (rate2 < rate1) {
					m_pTE->a.OutputSamplingFrequency.Set((float)nSamplesPerSec);
					nSamplesPerSec /= 2;
				}
			}

			switch (profile) {
				default:
				case 0:
					m_pTE->CodecID.Set("A_AAC/MPEG2/MAIN");
					break;
				case 1:
					m_pTE->CodecID.Set("A_AAC/MPEG2/LC");
					break;
				case 2:
					m_pTE->CodecID.Set("A_AAC/MPEG2/SSR");
					break;
				case 3:
					m_pTE->CodecID.Set("A_AAC/MPEG4/LTP");
					break;
				case 4:
					m_pTE->CodecID.Set("A_AAC/MPEG4/LC/SBR");
					break;
			}

			ASSERT(channels == wfe->nChannels);

			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)nSamplesPerSec);
			m_pTE->a.Channels.Set(channels);
			m_pTE->a.BitDepth.Set(wfe->wBitsPerSample);

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_WaveFormatEx
				   && ((WAVEFORMATEX*)m_mt.pbFormat)->wFormatTag == WAVE_FORMAT_DOLBY_AC3) {
			m_pTE->CodecID.Set("A_AC3");

			WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)wfe->nSamplesPerSec);
			m_pTE->a.Channels.Set(wfe->nChannels);
			m_pTE->a.BitDepth.Set(wfe->wBitsPerSample);

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_WaveFormatEx
				   && ((WAVEFORMATEX*)m_mt.pbFormat)->wFormatTag == WAVE_FORMAT_DTS2) {
			m_pTE->CodecID.Set("A_DTS");

			WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)wfe->nSamplesPerSec);
			m_pTE->a.Channels.Set(wfe->nChannels);
			m_pTE->a.BitDepth.Set(wfe->wBitsPerSample);

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_WaveFormatEx
				   && ((WAVEFORMATEX*)m_mt.pbFormat)->wFormatTag == WAVE_FORMAT_FLAC) {
			m_pTE->CodecID.Set("A_FLAC");

			WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)wfe->nSamplesPerSec);
			m_pTE->a.Channels.Set(wfe->nChannels);
			m_pTE->a.BitDepth.Set(wfe->wBitsPerSample);

			if (wfe->cbSize) {
				m_pTE->CodecPrivate.SetCount(wfe->cbSize);
				memcpy(m_pTE->CodecPrivate, m_mt.pbFormat + sizeof(WAVEFORMATEX), wfe->cbSize);
			}

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_WaveFormatEx
				   && (m_mt.subtype == MEDIASUBTYPE_14_4
					   || m_mt.subtype == MEDIASUBTYPE_28_8
					   || m_mt.subtype == MEDIASUBTYPE_ATRC
					   || m_mt.subtype == MEDIASUBTYPE_COOK
					   || m_mt.subtype == MEDIASUBTYPE_DNET
					   || m_mt.subtype == MEDIASUBTYPE_SIPR)) {
			CStringA id;
			id.Format("A_REAL/%c%c%c%c",
					  (char)((m_mt.subtype.Data1 >> 0) & 0xff),
					  (char)((m_mt.subtype.Data1 >> 8) & 0xff),
					  (char)((m_mt.subtype.Data1 >> 16) & 0xff),
					  (char)((m_mt.subtype.Data1 >> 24) & 0xff));

			m_pTE->CodecID.Set(id);

			WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
			DWORD cbSize = sizeof(WAVEFORMATEX) + wfe->cbSize;
			if (m_mt.cbFormat > cbSize) {
				m_pTE->CodecPrivate.SetCount(m_mt.cbFormat - cbSize);
				memcpy(m_pTE->CodecPrivate, m_mt.pbFormat + cbSize, m_pTE->CodecPrivate.GetCount());
			}
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)wfe->nSamplesPerSec);
			m_pTE->a.Channels.Set(wfe->nChannels);
			m_pTE->a.BitDepth.Set(wfe->wBitsPerSample);

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_WaveFormatEx
				   && ((WAVEFORMATEX*)m_mt.pbFormat)->wFormatTag == WAVE_FORMAT_PCM) {
			m_pTE->CodecID.Set("A_PCM/INT/LIT");

			WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)wfe->nSamplesPerSec);
			m_pTE->a.Channels.Set(wfe->nChannels);
			m_pTE->a.BitDepth.Set(wfe->wBitsPerSample);

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_WaveFormatEx) {
			m_pTE->CodecID.Set("A_MS/ACM");

			WAVEFORMATEX* wfe = (WAVEFORMATEX*)m_mt.pbFormat;
			m_pTE->CodecPrivate.SetCount(m_mt.cbFormat);
			memcpy(m_pTE->CodecPrivate, wfe, m_pTE->CodecPrivate.GetCount());
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)wfe->nSamplesPerSec);
			m_pTE->a.Channels.Set(wfe->nChannels);
			m_pTE->a.BitDepth.Set(wfe->wBitsPerSample);

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_VorbisFormat) {
			m_pTE->CodecID.Set("A_VORBIS");

			VORBISFORMAT* pvf = (VORBISFORMAT*)m_mt.pbFormat;
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)pvf->nSamplesPerSec);
			m_pTE->a.Channels.Set(pvf->nChannels);

			// m_pTE->CodecPrivate will be filled later

			hr = S_OK;
		} else if (m_mt.formattype == FORMAT_VorbisFormat2) {
			m_pTE->CodecID.Set("A_VORBIS");

			VORBISFORMAT2* pvf2 = (VORBISFORMAT2*)m_mt.pbFormat;
			m_pTE->DescType = TrackEntry::DescAudio;
			m_pTE->a.SamplingFrequency.Set((float)pvf2->SamplesPerSec);
			m_pTE->a.Channels.Set(pvf2->Channels);
			m_pTE->a.BitDepth.Set(pvf2->BitsPerSample);

			int len = 1;
			for (int i = 0; i < 2; i++) {
				len += pvf2->HeaderSize[i] / 255 + 1;
			}
			for (int i = 0; i < 3; i++) {
				len += pvf2->HeaderSize[i];
			}
			m_pTE->CodecPrivate.SetCount(len);

			BYTE* src = (BYTE*)m_mt.pbFormat + sizeof(VORBISFORMAT2);
			BYTE* dst = m_pTE->CodecPrivate.GetData();

			*dst++ = 2;
			for (int i = 0; i < 2; i++) {
				for (int len2 = pvf2->HeaderSize[i]; len2 >= 0; len2 -= 255) {
					*dst++ = (BYTE)(std::min)(len2, BYTE_MAX);
				}
			}

			memcpy(dst, src, pvf2->HeaderSize[0]);
			dst += pvf2->HeaderSize[0];
			src += pvf2->HeaderSize[0];
			memcpy(dst, src, pvf2->HeaderSize[1]);
			dst += pvf2->HeaderSize[1];
			src += pvf2->HeaderSize[1];
			memcpy(dst, src, pvf2->HeaderSize[2]);
			dst += pvf2->HeaderSize[2];
			src += pvf2->HeaderSize[2];

			ASSERT(src <= m_mt.pbFormat + m_mt.cbFormat);
			ASSERT(dst <= m_pTE->CodecPrivate.GetData() + m_pTE->CodecPrivate.GetCount());

			hr = S_OK;
		}
	} else if (m_mt.majortype == MEDIATYPE_Text) {
		m_pTE->TrackType.Set(TrackEntry::TypeSubtitle);

		if (m_mt.formattype == FORMAT_None) {
			m_pTE->CodecID.Set("S_TEXT/ASCII");
			hr = S_OK;
		}
	} else if (m_mt.majortype == MEDIATYPE_Subtitle) {
		m_pTE->TrackType.Set(TrackEntry::TypeSubtitle);

		m_pTE->CodecID.Set(
			m_mt.subtype == MEDIASUBTYPE_UTF8 ? "S_TEXT/UTF8" :
			m_mt.subtype == MEDIASUBTYPE_SSA ? "S_TEXT/SSA" :
			m_mt.subtype == MEDIASUBTYPE_ASS ? "S_TEXT/ASS" :
			m_mt.subtype == MEDIASUBTYPE_USF ? "S_TEXT/USF" :
			m_mt.subtype == MEDIASUBTYPE_VOBSUB ? "S_VOBSUB" :
			"");

		if (!m_pTE->CodecID.IsEmpty()) {
			hr = S_OK;

			SUBTITLEINFO* psi = (SUBTITLEINFO*)m_mt.pbFormat;
			if (psi->dwOffset) {
				m_pTE->CodecPrivate.SetCount(m_mt.cbFormat - psi->dwOffset);
				memcpy(m_pTE->CodecPrivate, m_mt.pbFormat + psi->dwOffset, m_pTE->CodecPrivate.GetCount());
			}
		}
	}

	if (S_OK == hr) {
		(static_cast<CMatroskaMuxerFilter*>(m_pFilter))->AddInput();
	}

	return hr;
}

HRESULT CMatroskaMuxerInputPin::Active()
{
	m_fActive = true;
	m_rtLastStart = m_rtLastStop = -1;
	m_fEndOfStreamReceived = false;
	return __super::Active();
}

HRESULT CMatroskaMuxerInputPin::Inactive()
{
	m_fActive = false;
	CAutoLock cAutoLock(&m_csQueue);
	m_blocks.RemoveAll();
	m_pVorbisHdrs.RemoveAll();
	return __super::Inactive();
}

STDMETHODIMP CMatroskaMuxerInputPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
	CAutoLock cAutoLock(&m_csReceive);
	return __super::NewSegment(tStart, tStop, dRate);
}

STDMETHODIMP CMatroskaMuxerInputPin::BeginFlush()
{
	return __super::BeginFlush();
}

STDMETHODIMP CMatroskaMuxerInputPin::EndFlush()
{
	return __super::EndFlush();
}

STDMETHODIMP CMatroskaMuxerInputPin::Receive(IMediaSample* pSample)
{
	if (m_fEndOfStreamReceived) {
		/*ASSERT(0);*/
		return S_FALSE;
	}

	CAutoLock cAutoLock(&m_csReceive);

	while (m_fActive) {
		{
			CAutoLock cAutoLock2(&m_csQueue);
			if (m_blocks.GetCount() < MAXBLOCKS) {
				break;
			}
		}

		Sleep(1);
	}

	if (!m_fActive) {
		return S_FALSE;
	}

	HRESULT hr;

	if (FAILED(hr = __super::Receive(pSample))) {
		return hr;
	}

	BYTE* pData = NULL;
	if (FAILED(hr = pSample->GetPointer(&pData)) || !pData) {
		return hr;
	}

	long inputLen = pSample->GetActualDataLength();

	REFERENCE_TIME rtStart = -1, rtStop = -1;
	hr = pSample->GetTime(&rtStart, &rtStop);

	if (FAILED(hr) || rtStart == -1 || rtStop == -1) {
		TRACE(_T("No timestamp was set on the sample!!!"));
		m_pFilter->NotifyEvent(EC_ERRORABORT, VFW_E_SAMPLE_TIME_NOT_SET, 0);
		return VFW_E_SAMPLE_TIME_NOT_SET;
	}

	//rtStart += m_tStart;
	//rtStop += m_tStart;

	TRACE(_T("Received (%u): %I64d-%I64d (c=%d, co=%dms), len=%ld, d%d p%d s%d\n"),
		  (static_cast<CMatroskaMuxerFilter*>(m_pFilter))->GetTrackNumber(this),
		  rtStart, rtStop, (int)((rtStart / 10000) / MAXCLUSTERTIME), (int)((rtStart / 10000) % MAXCLUSTERTIME),
		  inputLen,
		  pSample->IsDiscontinuity() == S_OK ? 1 : 0,
		  pSample->IsPreroll() == S_OK ? 1 : 0,
		  pSample->IsSyncPoint() == S_OK ? 1 : 0);

	if (m_mt.subtype == MEDIASUBTYPE_Vorbis && m_pVorbisHdrs.GetCount() < 3) {
		CAutoPtr<CBinary> data(DNew CBinary(0));
		data->SetCount(inputLen);
		memcpy(data->GetData(), pData, inputLen);
		m_pVorbisHdrs.Add(data);

		if (m_pVorbisHdrs.GetCount() == 3) {
			int len = 1;
			for (size_t i = 0; i < 2; i++) {
				len += (int)m_pVorbisHdrs[i]->GetCount() / 255 + 1;
			}
			for (size_t i = 0; i < 3; i++) {
				len += (int)m_pVorbisHdrs[i]->GetCount();
			}
			m_pTE->CodecPrivate.SetCount(len);

			BYTE* dst = m_pTE->CodecPrivate.GetData();

			*dst++ = 2;
			for (size_t i = 0; i < 2; i++) {
				for (INT_PTR len2 = m_pVorbisHdrs[i]->GetCount(); len2 >= 0; len2 -= 255) {
					*dst++ = (BYTE)std::min<INT_PTR>(len2, 255);
				}
			}

			for (size_t i = 0; i < 3; i++) {
				memcpy(dst, m_pVorbisHdrs[i]->GetData(), m_pVorbisHdrs[i]->GetCount());
				dst += m_pVorbisHdrs[i]->GetCount();
			}
		}

		return S_OK;
	}

	if (m_mt.formattype == FORMAT_WaveFormatEx
			&& (((WAVEFORMATEX*)m_mt.pbFormat)->wFormatTag == WAVE_FORMAT_PCM
				|| ((WAVEFORMATEX*)m_mt.pbFormat)->wFormatTag == WAVE_FORMAT_MPEGLAYER3)) {
		pSample->SetSyncPoint(TRUE);    // HACK: some capture filters don't set this
	}

	CAutoPtr<BlockGroup> b(DNew BlockGroup());
	/*
			// TODO: test this with a longer capture (pcm, mp3)
			if (S_OK == pSample->IsSyncPoint() && rtStart < m_rtLastStart)
			{
				TRACE(_T("!!! timestamp went backwards, dropping this frame !!! rtStart (%I64) < m_rtLastStart (%I64)"), rtStart, m_rtLastStart);
				return S_OK;
			}
	*/
	if ((S_OK != pSample->IsSyncPoint() || m_rtLastStart == rtStart) && m_rtLastStart >= 0 /*&& m_rtLastStart < rtStart*/) {
		ASSERT(m_rtLastStart - rtStart <= 0);
		REFERENCE_TIME rtDiff = m_rtLastStart - rtStart;
		b->ReferenceBlock.Set((rtDiff + (rtDiff >= 0 ? 5000 : -5000)) / 10000);
	}

	b->Block.TrackNumber = (static_cast<CMatroskaMuxerFilter*>(m_pFilter))->GetTrackNumber(this);

	b->Block.TimeCode = (rtStart + 5000) / 10000;
	b->Block.TimeCodeStop = (rtStop + 5000) / 10000;

	if (m_pTE->TrackType == TrackEntry::TypeSubtitle) {
		b->BlockDuration.Set((rtStop - rtStart + 5000) / 10000);
	}

	CAutoPtr<CBinary> data(DNew CBinary(0));
	data->SetCount(inputLen);
	memcpy(data->GetData(), pData, inputLen);
	b->Block.BlockData.AddTail(data);

	CAutoLock cAutoLock2(&m_csQueue);
	m_blocks.AddTail(b); // TODO: lacing for audio

	m_rtLastStart = rtStart;
	m_rtLastStop = rtStop;

	return S_OK;
}

STDMETHODIMP CMatroskaMuxerInputPin::EndOfStream()
{
	HRESULT hr;

	if (FAILED(hr = __super::EndOfStream())) {
		return hr;
	}

	CAutoLock cAutoLock(&m_csQueue);

	m_fEndOfStreamReceived = true;

	return hr;
}

//
// CMatroskaMuxerOutputPin
//

CMatroskaMuxerOutputPin::CMatroskaMuxerOutputPin(TCHAR* pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
	: CBaseOutputPin(pName, pFilter, pLock, phr, L"Output")
{
}

CMatroskaMuxerOutputPin::~CMatroskaMuxerOutputPin()
{
}

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

	return
		__super::NonDelegatingQueryInterface(riid, ppv);
}

HRESULT CMatroskaMuxerOutputPin::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProperties)
{
	ASSERT(pAlloc);
	ASSERT(pProperties);

	HRESULT hr = NOERROR;

	pProperties->cBuffers = 1;
	pProperties->cbBuffer = 1;

	ALLOCATOR_PROPERTIES Actual;
	if (FAILED(hr = pAlloc->SetProperties(pProperties, &Actual))) {
		return hr;
	}

	if (Actual.cbBuffer < pProperties->cbBuffer) {
		return E_FAIL;
	}
	ASSERT(Actual.cBuffers == pProperties->cBuffers);

	return NOERROR;
}

HRESULT CMatroskaMuxerOutputPin::CheckMediaType(const CMediaType* pmt)
{
	return pmt->majortype == MEDIATYPE_Stream && pmt->subtype == MEDIASUBTYPE_Matroska
		   ? S_OK
		   : E_INVALIDARG;
}

HRESULT CMatroskaMuxerOutputPin::GetMediaType(int iPosition, CMediaType* pmt)
{
	CAutoLock cAutoLock(m_pLock);

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

	pmt->ResetFormatBuffer();
	pmt->InitMediaType();
	pmt->majortype = MEDIATYPE_Stream;
	pmt->subtype = MEDIASUBTYPE_Matroska;
	pmt->formattype = FORMAT_None;

	return S_OK;
}

STDMETHODIMP CMatroskaMuxerOutputPin::Notify(IBaseFilter* pSender, Quality q)
{
	return E_NOTIMPL;
}
