Code Search for Developers
 
 
  

MP4Splitter.cpp from guliverkli at Krugle


Show MP4Splitter.cpp syntax highlighted

/* 
 *	Copyright (C) 2003-2006 Gabest
 *	http://www.gabest.org
 *
 *  This Program 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 2, 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 General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "StdAfx.h"
#include "Mmreg.h"
#include "MP4Splitter.h"
#include "..\..\..\DSUtil\DSUtil.h"

#include <initguid.h>
#include "..\..\..\..\include\moreuuids.h"

#include "Ap4.h"
#include "Ap4File.h"
#include "Ap4StssAtom.h"
#include "Ap4StsdAtom.h"
#include "Ap4IsmaCryp.h"
#include "Ap4AvcCAtom.h"
#include "Ap4ChplAtom.h"
#include "Ap4FtabAtom.h"
#include "Ap4DataAtom.h"

#ifdef REGISTER_FILTER

const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] =
{
	{&MEDIATYPE_Stream, &MEDIASUBTYPE_MP4},
	{&MEDIATYPE_Stream, &MEDIASUBTYPE_NULL},
};

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

const AMOVIESETUP_FILTER sudFilter[] =
{
	{&__uuidof(CMP4SplitterFilter), L"MP4 Splitter", MERIT_NORMAL, countof(sudpPins), sudpPins},
	{&__uuidof(CMP4SourceFilter), L"MP4 Source", MERIT_NORMAL, 0, NULL},
	{&__uuidof(CMPEG4VideoSplitterFilter), L"MPEG4 Video Splitter", MERIT_NORMAL, countof(sudpPins), sudpPins},
	{&__uuidof(CMPEG4VideoSourceFilter), L"MPEG4 Video Source", MERIT_NORMAL, 0, NULL},	
};

CFactoryTemplate g_Templates[] =
{
	{sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CMP4SplitterFilter>, NULL, &sudFilter[0]},
	{sudFilter[1].strName, sudFilter[1].clsID, CreateInstance<CMP4SourceFilter>, NULL, &sudFilter[1]},
	{sudFilter[2].strName, sudFilter[2].clsID, CreateInstance<CMPEG4VideoSplitterFilter>, NULL, &sudFilter[2]},
	{sudFilter[3].strName, sudFilter[3].clsID, CreateInstance<CMPEG4VideoSourceFilter>, NULL, &sudFilter[3]},
};

int g_cTemplates = countof(g_Templates);

STDAPI DllRegisterServer()
{
	DeleteRegKey(_T("Media Type\\Extensions\\"), _T(".mp4"));
	DeleteRegKey(_T("Media Type\\Extensions\\"), _T(".mov"));

	CAtlList<CString> chkbytes;

	// mp4
	chkbytes.AddTail(_T("4,4,,66747970")); // ftyp
	chkbytes.AddTail(_T("4,4,,6d6f6f76")); // moov
	chkbytes.AddTail(_T("4,4,,6d646174")); // mdat
	chkbytes.AddTail(_T("4,4,,736b6970")); // skip
	chkbytes.AddTail(_T("4,12,ffffffff00000000ffffffff,77696465027fe3706d646174")); // wide ? mdat

	// mpeg4 video
	chkbytes.AddTail(_T("3,3,,000001"));

	RegisterSourceFilter(CLSID_AsyncReader, MEDIASUBTYPE_MP4, chkbytes, NULL);

	return AMovieDllRegisterServer2(TRUE);
}

STDAPI DllUnregisterServer()
{
	UnRegisterSourceFilter(MEDIASUBTYPE_MP4);

	return AMovieDllRegisterServer2(FALSE);
}

#include "..\..\FilterApp.h"

CFilterApp theApp;

#endif

//
// CMP4SplitterFilter
//

CMP4SplitterFilter::CMP4SplitterFilter(LPUNKNOWN pUnk, HRESULT* phr)
	: CBaseSplitterFilter(NAME("CMP4SplitterFilter"), pUnk, phr, __uuidof(this))
{
}

CMP4SplitterFilter::~CMP4SplitterFilter()
{
}

HRESULT CMP4SplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader)
{
	CheckPointer(pAsyncReader, E_POINTER);

	HRESULT hr = E_FAIL;

	m_trackpos.RemoveAll();

	m_pFile.Free();
	m_pFile.Attach(new CMP4SplitterFile(pAsyncReader, hr));
	if(!m_pFile) return E_OUTOFMEMORY;
	if(FAILED(hr)) {m_pFile.Free(); return hr;}

	m_rtNewStart = m_rtCurrent = 0;
	m_rtNewStop = m_rtStop = m_rtDuration = 0;

	m_framesize.SetSize(640, 480);

	if(AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie())
	{
		for(AP4_List<AP4_Track>::Item* item = movie->GetTracks().FirstItem();
			item;
			item = item->GetNext())
		{
			AP4_Track* track = item->GetData();

			if(track->GetType() != AP4_Track::TYPE_VIDEO 
			&& track->GetType() != AP4_Track::TYPE_AUDIO
			&& track->GetType() != AP4_Track::TYPE_TEXT
			&& track->GetType() != AP4_Track::TYPE_SUBP)
				continue;

			AP4_Sample sample;

			if(!AP4_SUCCEEDED(track->GetSample(0, sample)) || sample.GetDescriptionIndex() == 0xFFFFFFFF)
				continue;

			CStringW TrackName = UTF8To16(track->GetTrackName().c_str());
			CStringA TrackLanguage = track->GetTrackLanguage().c_str();

			CAtlArray<CMediaType> mts;

			CMediaType mt;
			mt.SetSampleSize(1);

			VIDEOINFOHEADER* vih = NULL;
			WAVEFORMATEX* wfe = NULL;

			AP4_DataBuffer empty;

			if(AP4_SampleDescription* desc = track->GetSampleDescription(sample.GetDescriptionIndex()))
			{
				AP4_MpegSampleDescription* mpeg_desc = NULL;

				if(desc->GetType() == AP4_SampleDescription::TYPE_MPEG)
				{
					mpeg_desc = dynamic_cast<AP4_MpegSampleDescription*>(desc);
				}
				else if(desc->GetType() == AP4_SampleDescription::TYPE_ISMACRYP)
				{
					AP4_IsmaCrypSampleDescription* isma_desc = dynamic_cast<AP4_IsmaCrypSampleDescription*>(desc);
					mpeg_desc = isma_desc->GetOriginalSampleDescription();
				}

				if(AP4_MpegVideoSampleDescription* video_desc = 
					dynamic_cast<AP4_MpegVideoSampleDescription*>(mpeg_desc))
				{
					const AP4_DataBuffer* di = video_desc->GetDecoderInfo();
					if(!di) di = &empty;

					mt.majortype = MEDIATYPE_Video;
					mt.formattype = FORMAT_VideoInfo;
					vih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + di->GetDataSize());
					memset(vih, 0, mt.FormatLength());
					vih->dwBitRate = video_desc->GetAvgBitrate()/8;
					vih->bmiHeader.biSize = sizeof(vih->bmiHeader);
					vih->bmiHeader.biWidth = (LONG)video_desc->GetWidth();
					vih->bmiHeader.biHeight = (LONG)video_desc->GetHeight();
					memcpy(vih + 1, di->GetData(), di->GetDataSize());

					switch(video_desc->GetObjectTypeId())
					{
					case AP4_MPEG4_VISUAL_OTI:
						mt.subtype = FOURCCMap('v4pm');
						mt.formattype = FORMAT_MPEG2Video;
						{
						MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + di->GetDataSize());
						memset(vih, 0, mt.FormatLength());
						vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader);
						vih->hdr.bmiHeader.biWidth = (LONG)video_desc->GetWidth();
						vih->hdr.bmiHeader.biHeight = (LONG)video_desc->GetHeight();
						vih->hdr.bmiHeader.biCompression = 'v4pm';
						vih->hdr.bmiHeader.biPlanes = 1;
						vih->hdr.bmiHeader.biBitCount = 24;
						vih->hdr.dwPictAspectRatioX = vih->hdr.bmiHeader.biWidth;
						vih->hdr.dwPictAspectRatioY = vih->hdr.bmiHeader.biHeight;
						vih->cbSequenceHeader = di->GetDataSize();
						memcpy(vih->dwSequenceHeader, di->GetData(), di->GetDataSize());
						mts.Add(mt);
						mt.subtype = FOURCCMap(vih->hdr.bmiHeader.biCompression = 'V4PM');
						mts.Add(mt);
						}
						break;
					case AP4_MPEG2_VISUAL_SIMPLE_OTI:
					case AP4_MPEG2_VISUAL_MAIN_OTI:
					case AP4_MPEG2_VISUAL_SNR_OTI:
					case AP4_MPEG2_VISUAL_SPATIAL_OTI:
					case AP4_MPEG2_VISUAL_HIGH_OTI:
					case AP4_MPEG2_VISUAL_422_OTI:
						mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
						{
						m_pFile->Seek(sample.GetOffset());
						CBaseSplitterFileEx::seqhdr h;
						CMediaType mt2;
						if(m_pFile->Read(h, sample.GetSize(), &mt2))
							mt = mt2;
						}
						mts.Add(mt);
						break;
					case AP4_MPEG1_VISUAL_OTI: // ???
						mt.subtype = MEDIASUBTYPE_MPEG1Payload;
						{
						m_pFile->Seek(sample.GetOffset());
						CBaseSplitterFileEx::seqhdr h;
						CMediaType mt2;
						if(m_pFile->Read(h, sample.GetSize(), &mt2))
							mt = mt2;
						}
						mts.Add(mt);
						break;
					}

					if(mt.subtype == GUID_NULL)
					{
						TRACE(_T("Unknown video OBI: %02x\n"), video_desc->GetObjectTypeId());
					}
				}
				else if(AP4_MpegAudioSampleDescription* audio_desc = 
					dynamic_cast<AP4_MpegAudioSampleDescription*>(mpeg_desc))
				{
					const AP4_DataBuffer* di = audio_desc->GetDecoderInfo();
					if(!di) di = &empty;

					mt.majortype = MEDIATYPE_Audio;
					mt.formattype = FORMAT_WaveFormatEx;

					wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX) + di->GetDataSize());
					memset(wfe, 0, mt.FormatLength());
					wfe->nSamplesPerSec = audio_desc->GetSampleRate();
					wfe->nAvgBytesPerSec = audio_desc->GetAvgBitrate()/8;
					wfe->nChannels = audio_desc->GetChannelCount();
					wfe->wBitsPerSample = audio_desc->GetSampleSize();
					wfe->cbSize = (WORD)di->GetDataSize();
					memcpy(wfe + 1, di->GetData(), di->GetDataSize());

					switch(audio_desc->GetObjectTypeId())
					{
					case AP4_MPEG4_AUDIO_OTI:
					case AP4_MPEG2_AAC_AUDIO_MAIN_OTI: // ???
					case AP4_MPEG2_AAC_AUDIO_LC_OTI: // ???
					case AP4_MPEG2_AAC_AUDIO_SSRP_OTI: // ???
						mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_AAC);
						if(wfe->cbSize >= 2) wfe->nChannels = (((BYTE*)(wfe+1))[1]>>3) & 0xf;
						mts.Add(mt);
						break;
					case AP4_MPEG2_PART3_AUDIO_OTI: // ???
					case AP4_MPEG1_AUDIO_OTI:
						mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_MP3);
						{
						m_pFile->Seek(sample.GetOffset());
						CBaseSplitterFileEx::mpahdr h;
						CMediaType mt2;
						if(m_pFile->Read(h, sample.GetSize(), false, &mt2))
							mt = mt2;
						}
						mts.Add(mt);
						break;
					}

					if(mt.subtype == GUID_NULL)
					{
						TRACE(_T("Unknown audio OBI: %02x\n"), audio_desc->GetObjectTypeId());
					}
				}
				else if(AP4_MpegSystemSampleDescription* system_desc = 
					dynamic_cast<AP4_MpegSystemSampleDescription*>(desc))
				{
					const AP4_DataBuffer* di = system_desc->GetDecoderInfo();
					if(!di) di = &empty;

					switch(system_desc->GetObjectTypeId())
					{
					case AP4_NERO_VOBSUB:
						if(di->GetDataSize() >= 16*4)
						{
							CSize size(720, 576);
							if(AP4_TkhdAtom* tkhd = dynamic_cast<AP4_TkhdAtom*>(track->GetTrakAtom()->GetChild(AP4_ATOM_TYPE_TKHD)))
							{
								size.cx = tkhd->GetWidth()>>16;
								size.cy = tkhd->GetHeight()>>16;
							}

							const AP4_Byte* pal = di->GetData();
							CAtlList<CStringA> sl;
							for(int i = 0; i < 16*4; i += 4)
							{
								BYTE y = (pal[i+1]-16)*255/219;
								BYTE u = pal[i+2];
								BYTE v = pal[i+3];
								BYTE r = (BYTE)min(max(1.0*y + 1.4022*(v-128), 0), 255);
								BYTE g = (BYTE)min(max(1.0*y - 0.3456*(u-128) - 0.7145*(v-128), 0), 255);
								BYTE b = (BYTE)min(max(1.0*y + 1.7710*(u-128), 0) , 255);
								CStringA str;
								str.Format("%02x%02x%02x", r, g, b);
								sl.AddTail(str);
							}

							CStringA hdr;
							hdr.Format(
								"# VobSub index file, v7 (do not modify this line!)\n"
								"size: %dx%d\n"
								"palette: %s\n",
								size.cx, size.cy,
								Implode(sl, ','));
							
							mt.majortype = MEDIATYPE_Subtitle;
							mt.subtype = MEDIASUBTYPE_VOBSUB;
							mt.formattype = FORMAT_SubtitleInfo;
							SUBTITLEINFO* si = (SUBTITLEINFO*)mt.AllocFormatBuffer(sizeof(SUBTITLEINFO) + hdr.GetLength());
							memset(si, 0, mt.FormatLength());
							si->dwOffset = sizeof(SUBTITLEINFO);
							strcpy_s(si->IsoLang, countof(si->IsoLang), CStringA(TrackLanguage));
							wcscpy_s(si->TrackName, countof(si->TrackName), TrackName);
							memcpy(si + 1, (LPCSTR)hdr, hdr.GetLength());
							mts.Add(mt);
						}
						break;
					}

					if(mt.subtype == GUID_NULL)
					{
						TRACE(_T("Unknown audio OBI: %02x\n"), system_desc->GetObjectTypeId());
					}
				}
				else if(AP4_UnknownSampleDescription* unknown_desc = 
					dynamic_cast<AP4_UnknownSampleDescription*>(desc)) // TEMP
				{
					AP4_SampleEntry* sample_entry = unknown_desc->GetSampleEntry();

					if(dynamic_cast<AP4_TextSampleEntry*>(sample_entry)
					|| dynamic_cast<AP4_Tx3gSampleEntry*>(sample_entry))
					{
						mt.majortype = MEDIATYPE_Subtitle;
						mt.subtype = MEDIASUBTYPE_ASS2;
						mt.formattype = FORMAT_SubtitleInfo;
						CStringA hdr;
						hdr.Format(
							"[Script Info]\n"
							"ScriptType: v4.00++\n"
							"ScaledBorderAndShadow: yes\n"
							"PlayResX: %d\n"
							"PlayResY: %d\n"
							"[V4++ Styles]\n"
							"Style: Text,Arial,12,&H00ffffff,&H0000ffff,&H00000000,&H80000000,0,0,0,0,100,100,0,0.00,3,0,0,2,0,0,0,0,1,1\n",
							// "Style: Text,Arial,12,&H00ffffff,&H0000ffff,&H00000000,&H80000000,0,0,0,0,100,100,0,0.00,1,0,0,2,0,0,0,0,1,1\n",
							m_framesize.cx, 
							m_framesize.cy);
						SUBTITLEINFO* si = (SUBTITLEINFO*)mt.AllocFormatBuffer(sizeof(SUBTITLEINFO) + hdr.GetLength());
						memset(si, 0, mt.FormatLength());
						si->dwOffset = sizeof(SUBTITLEINFO);
						strcpy_s(si->IsoLang, countof(si->IsoLang), CStringA(TrackLanguage));
						wcscpy_s(si->TrackName, countof(si->TrackName), TrackName);
						memcpy(si + 1, (LPCSTR)hdr, hdr.GetLength());
						mts.Add(mt);
					}
				}
			}
			else if(AP4_Avc1SampleEntry* avc1 = dynamic_cast<AP4_Avc1SampleEntry*>(
				track->GetTrakAtom()->FindChild("mdia/minf/stbl/stsd/avc1")))
			{
				if(AP4_AvcCAtom* avcC = dynamic_cast<AP4_AvcCAtom*>(avc1->GetChild(AP4_ATOM_TYPE_AVCC)))
				{
					const AP4_DataBuffer* di = avcC->GetDecoderInfo();
					if(!di) di = &empty;

					const AP4_Byte* data = di->GetData();
					AP4_Size size = di->GetDataSize();

					mt.majortype = MEDIATYPE_Video;
					mt.subtype = FOURCCMap('1cva');
					mt.formattype = FORMAT_MPEG2Video;

					MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + size - 7);
					memset(vih, 0, mt.FormatLength());
					vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader);
					vih->hdr.bmiHeader.biWidth = (LONG)avc1->GetWidth();
					vih->hdr.bmiHeader.biHeight = (LONG)avc1->GetHeight();
					vih->hdr.bmiHeader.biCompression = '1cva';
					vih->hdr.bmiHeader.biPlanes = 1;
					vih->hdr.bmiHeader.biBitCount = 24;
					vih->hdr.dwPictAspectRatioX = vih->hdr.bmiHeader.biWidth;
					vih->hdr.dwPictAspectRatioY = vih->hdr.bmiHeader.biHeight;
					vih->dwProfile = data[1];
					vih->dwLevel = data[3];
					vih->dwFlags = (data[4] & 3) + 1;

					vih->cbSequenceHeader = 0;

					BYTE* src = (BYTE*)data + 5;
					BYTE* dst = (BYTE*)vih->dwSequenceHeader;

					BYTE* src_end = (BYTE*)data + size;
					BYTE* dst_end = (BYTE*)vih->dwSequenceHeader + size;

					for(int i = 0; i < 2; i++)
					{
						for(int n = *src++ & 0x1f; n > 0; n--)
						{
							int len = ((src[0] << 8) | src[1]) + 2;
							if(src + len > src_end || dst + len > dst_end) {ASSERT(0); break;}
							memcpy(dst, src, len);
							src += len; 
							dst += len;
							vih->cbSequenceHeader += len;
						}
					}

					mts.Add(mt);

					mt.subtype = FOURCCMap(vih->hdr.bmiHeader.biCompression = '1CVA');
					mts.Add(mt);
				}
			}
			else if(AP4_StsdAtom* stsd = dynamic_cast<AP4_StsdAtom*>(
				track->GetTrakAtom()->FindChild("mdia/minf/stbl/stsd")))
			{
				const AP4_DataBuffer& db = stsd->GetDataBuffer();

				for(AP4_List<AP4_Atom>::Item* item = stsd->GetChildren().FirstItem(); 
					item; 
					item = item->GetNext())
				{
					AP4_Atom* atom = item->GetData();

					AP4_Atom::Type type = atom->GetType();

					if((type & 0xffff0000) == AP4_ATOM_TYPE('m', 's', 0, 0))
					{
						type &= 0xffff;
					}
					else if(type == AP4_ATOM_TYPE__MP3)
					{
						type = 0x0055;
					}
					else
					{
						type = 
							((type >> 24) & 0x000000ff) |
							((type >>  8) & 0x0000ff00) |
							((type <<  8) & 0x00ff0000) |
							((type << 24) & 0xff000000);
					}

					if(AP4_VisualSampleEntry* vse = dynamic_cast<AP4_VisualSampleEntry*>(atom))
					{
						mt.majortype = MEDIATYPE_Video;
						mt.subtype = FOURCCMap(type);
						mt.formattype = FORMAT_VideoInfo;
						vih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER)+db.GetDataSize());
						memset(vih, 0, mt.FormatLength());
						vih->bmiHeader.biSize = sizeof(vih->bmiHeader);
						vih->bmiHeader.biWidth = (LONG)vse->GetWidth();
						vih->bmiHeader.biHeight = (LONG)vse->GetHeight();
						vih->bmiHeader.biCompression = type;
						memcpy(vih+1, db.GetData(), db.GetDataSize());
						mts.Add(mt);

						char buff[5];
						memcpy(buff, &type, 4);
						buff[4] = 0;

						strlwr((char*)&buff);
						AP4_Atom::Type typelwr = *(AP4_Atom::Type*)buff;

						if(typelwr != type)
						{
							mt.subtype = FOURCCMap(vih->bmiHeader.biCompression = typelwr);
							mts.Add(mt);
						}

						strupr((char*)&buff);
						AP4_Atom::Type typeupr = *(AP4_Atom::Type*)buff;

						if(typeupr != type)
						{
							mt.subtype = FOURCCMap(vih->bmiHeader.biCompression = typeupr);
							mts.Add(mt);
						}

						break;
					}
					else if(AP4_AudioSampleEntry* ase = dynamic_cast<AP4_AudioSampleEntry*>(atom))
					{
						mt.majortype = MEDIATYPE_Audio;
						mt.subtype = FOURCCMap(type);
						mt.formattype = FORMAT_WaveFormatEx;
						wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX) + db.GetDataSize());
						memset(wfe, 0, mt.FormatLength());
						if(!(type & 0xffff0000)) wfe->wFormatTag = (WORD)type;
						wfe->nSamplesPerSec = ase->GetSampleRate();
						wfe->nChannels = ase->GetChannelCount();
						wfe->wBitsPerSample = ase->GetSampleSize();
						wfe->nBlockAlign = ase->GetBytesPerFrame();
						wfe->cbSize = db.GetDataSize();
						memcpy(wfe+1, db.GetData(), db.GetDataSize());
						mts.Add(mt);
						break;
					}
				}
			}

			if(mts.IsEmpty()) continue;

			REFERENCE_TIME rtDuration = 10000i64 * track->GetDurationMs();
			if(m_rtDuration < rtDuration) m_rtDuration = rtDuration;

			DWORD id = track->GetId();

			CStringW name, lang;
			name.Format(L"Output %d", id);

			if(!TrackName.IsEmpty())
			{
				name = TrackName;
			}

			if(!TrackLanguage.IsEmpty())
			{
				if(TrackLanguage != L"und") name += " (" + TrackLanguage + ")";
			}

			for(int i = 0, j = mts.GetCount(); i < j; i++)
			{
				BITMAPINFOHEADER bih;
				if(ExtractBIH(&mts[i], &bih))
				{
					m_framesize.cx = bih.biWidth;
					m_framesize.cy = abs(bih.biHeight);
				}
			}

			CAutoPtr<CBaseSplitterOutputPin> pPinOut(new CBaseSplitterOutputPin(mts, name, this, this, &hr));

			if(!TrackName.IsEmpty()) pPinOut->SetProperty(L"NAME", TrackName);
			if(!TrackLanguage.IsEmpty()) pPinOut->SetProperty(L"LANG", CStringW(TrackLanguage));

			EXECUTE_ASSERT(SUCCEEDED(AddOutputPin(id, pPinOut)));

			m_trackpos[id] = trackpos();

			if(mts.GetCount() == 1 && mts[0].subtype == MEDIASUBTYPE_ASS2)
			{
				LPCWSTR postfix = L" (plain text)";

				mts[0].subtype = MEDIASUBTYPE_UTF8;

				SUBTITLEINFO* si = (SUBTITLEINFO*)mts[0].ReallocFormatBuffer(sizeof(SUBTITLEINFO));
				wcscat(si->TrackName, postfix);

				id ^= 0x80402010; // FIXME: until fixing, let's hope there won't be another track like this...

				CAutoPtr<CBaseSplitterOutputPin> pPinOut(new CBaseSplitterOutputPin(mts, name + postfix, this, this, &hr));

				if(!TrackName.IsEmpty()) pPinOut->SetProperty(L"NAME", TrackName + postfix);
				if(!TrackLanguage.IsEmpty()) pPinOut->SetProperty(L"LANG", CStringW(TrackLanguage));

				EXECUTE_ASSERT(SUCCEEDED(AddOutputPin(id, pPinOut)));
			}
		}

		if(AP4_ChplAtom* chpl = dynamic_cast<AP4_ChplAtom*>(movie->GetMoovAtom()->FindChild("udta/chpl")))
		{
			AP4_Array<AP4_ChplAtom::AP4_Chapter>& chapters = chpl->GetChapters();

			for(AP4_Cardinal i = 0; i < chapters.ItemCount(); i++)
			{
				AP4_ChplAtom::AP4_Chapter& chapter = chapters[i];
				ChapAppend(chapter.Time, UTF8To16(ConvertMBCS(chapter.Name.c_str(), ANSI_CHARSET, CP_UTF8))); // this is b0rked, thx to nero :P
			}

			ChapSort();
		}

		if(AP4_ContainerAtom* ilst = dynamic_cast<AP4_ContainerAtom*>(movie->GetMoovAtom()->FindChild("udta/meta/ilst")))
		{
			CStringW title, artist, writer, album, year, appl, desc, gen, track;

			for(AP4_List<AP4_Atom>::Item* item = ilst->GetChildren().FirstItem();
				item;
				item = item->GetNext())
			{
				if(AP4_ContainerAtom* atom = dynamic_cast<AP4_ContainerAtom*>(item->GetData()))
				{
					if(AP4_DataAtom* data = dynamic_cast<AP4_DataAtom*>(atom->GetChild(AP4_ATOM_TYPE_DATA)))
					{
						const AP4_DataBuffer* db = data->GetData();

						if(atom->GetType() == AP4_ATOM_TYPE_TRKN)
						{
							if(db->GetDataSize() >= 4)
							{
								unsigned short n = (db->GetData()[2] << 8) | db->GetData()[3];
								if(n > 0 && n < 100) track.Format(L"%02d", n);
								else if(n >= 100) track.Format(L"%d", n);
							}
						}
						else
						{
							CStringW str = UTF8To16(CStringA((LPCSTR)db->GetData(), db->GetDataSize()));

							switch(atom->GetType())
							{
							case AP4_ATOM_TYPE_NAM: title = str; break;
							case AP4_ATOM_TYPE_ART: artist = str; break;
							case AP4_ATOM_TYPE_WRT: writer = str; break;
							case AP4_ATOM_TYPE_ALB: album = str; break;
							case AP4_ATOM_TYPE_DAY: year = str; break;
							case AP4_ATOM_TYPE_TOO: appl = str; break;
							case AP4_ATOM_TYPE_CMT: desc = str; break;
							case AP4_ATOM_TYPE_GEN: gen = str; break;
							}
						}
					}
				}
			}

			if(!title.IsEmpty())
			{
				if(!track.IsEmpty()) title = track + L" - " + title;
				if(!album.IsEmpty()) title = album + L" - " + title;
				if(!year.IsEmpty()) title += L" - " +  year;
				if(!gen.IsEmpty()) title += L" - " + gen;
				SetProperty(L"TITL", title);
			}

			if(!artist.IsEmpty()) SetProperty(L"AUTH", artist);
			else if(!writer.IsEmpty()) SetProperty(L"AUTH", writer);

			if(!appl.IsEmpty()) SetProperty(L"APPL", appl);

			if(!desc.IsEmpty()) SetProperty(L"DESC", desc);
		}
	}

	m_rtNewStop = m_rtStop = m_rtDuration;

	return m_pOutputs.GetCount() > 0 ? S_OK : E_FAIL;
}

bool CMP4SplitterFilter::DemuxInit()
{
	AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie();

	POSITION pos = m_trackpos.GetStartPosition();
	while(pos)
	{
		CAtlMap<DWORD, trackpos>::CPair* pPair = m_trackpos.GetNext(pos);

		pPair->m_value.index = 0;
		pPair->m_value.ts = 0;

		AP4_Track* track = movie->GetTrack(pPair->m_key);

		AP4_Sample sample;
		if(AP4_SUCCEEDED(track->GetSample(0, sample)))
			pPair->m_value.ts = sample.GetCts();
	}

	return true;
}

void CMP4SplitterFilter::DemuxSeek(REFERENCE_TIME rt)
{
	AP4_TimeStamp ts = (AP4_TimeStamp)(rt / 10000);

	AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie();

	POSITION pos = m_trackpos.GetStartPosition();
	while(pos)
	{
		CAtlMap<DWORD, trackpos>::CPair* pPair = m_trackpos.GetNext(pos);

		AP4_Track* track = movie->GetTrack(pPair->m_key);

		if(AP4_FAILED(track->GetSampleIndexForTimeStampMs(ts, pPair->m_value.index)))
			pPair->m_value.index = 0;

		AP4_Sample sample;
		if(AP4_SUCCEEDED(track->GetSample(pPair->m_value.index, sample)))
			pPair->m_value.ts = sample.GetCts();

		// FIXME: slow search & stss->m_Entries is private

		if(AP4_StssAtom* stss = dynamic_cast<AP4_StssAtom*>(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss")))
		{
			if(stss->m_Entries.ItemCount() > 0)
			{
				AP4_Cardinal i = -1;
				while(++i < stss->m_Entries.ItemCount() && stss->m_Entries[i]-1 <= pPair->m_value.index);
				if(i > 0) i--;
				pPair->m_value.index = stss->m_Entries[i]-1;
			}
		}
	}
}

struct SSACharacter {CString pre, post; WCHAR c;}; 

static CStringW ConvertTX3GToSSA(
	CStringW str, 
	const AP4_Tx3gSampleEntry::AP4_Tx3gDescription& desc, 
	CStringW font,
	const AP4_Byte* mods, 
	int size, 
	CSize framesize,
	CPoint translation,
	int durationms,
	CRect& rbox)
{
	int str_len = str.GetLength();

	SSACharacter* chars = new SSACharacter[str_len];
	for(int i = 0; i < str_len; i++) chars[i].c = str[i];
	str.Empty();

	//

	rbox.SetRect(desc.TextBox.Left, desc.TextBox.Top, desc.TextBox.Right, desc.TextBox.Bottom);

	int align = 2;
	signed char h = (signed char)desc.HorizontalJustification;
	signed char v = (signed char)desc.VerticalJustification;
	if(h == 0 && v < 0) align = 1;
	else if(h > 0 && v < 0) align = 2;
	else if(h < 0 && v < 0) align = 3;
	else if(h == 0 && v > 0) align = 4;
	else if(h > 0 && v > 0) align = 5;
	else if(h < 0 && v > 0) align = 6;
	else if(h == 0 && v == 0) align = 7;
	else if(h > 0 && v == 0) align = 8;
	else if(h < 0 && v == 0) align = 9;
	str.Format(L"{\\an%d}%s", align, CStringW(str));

	if(!font.CompareNoCase(L"serif")) font = L"Times New Roman";
	else if(!font.CompareNoCase(L"sans-serif")) font = L"Arial";
	else if(!font.CompareNoCase(L"monospace")) font = L"Courier New";
	str.Format(L"{\\fn%s}%s", font, CStringW(str));	

	const AP4_Byte* fclr = (const AP4_Byte*)&desc.Style.Font.Color;

	CStringW font_color;
	font_color.Format(L"{\\1c%02x%02x%02x\\1a%02x}", fclr[2], fclr[1], fclr[0], 255 - fclr[3]);
	str = font_color + str;

	str.Format(L"%s{\\2c%02x%02x%02x\\2a%02x}", CString(str), fclr[2], fclr[1], fclr[0], 255 - fclr[3]);

	CStringW font_size;
	font_size.Format(L"{\\fs%d}", desc.Style.Font.Size);
	str = font_size + str;

	CStringW font_flags;
	font_flags.Format(L"{\\b%d\\i%d\\u%d}", 
		(desc.Style.Font.Face&1) ? 1 : 0,
		(desc.Style.Font.Face&2) ? 1 : 0,
		(desc.Style.Font.Face&4) ? 1 : 0);
	str = font_flags + str;

	//

	const AP4_Byte* hclr = (const AP4_Byte*)&desc.BackgroundColor;

	while(size > 8)
	{
		DWORD tag_size = (mods[0]<<24)|(mods[1]<<16)|(mods[2]<<8)|(mods[3]); mods += 4;
		DWORD tag = (mods[0]<<24)|(mods[1]<<16)|(mods[2]<<8)|(mods[3]); mods += 4;

		size -= tag_size;
		tag_size -= 8;
		const AP4_Byte* next = mods + tag_size;

		if(tag == 'styl')
		{
			WORD styl_count = (mods[0]<<8)|(mods[1]); mods += 2;

			while(styl_count-- > 0)
			{
				WORD start = (mods[0]<<8)|(mods[1]); mods += 2;
				WORD end = (mods[0]<<8)|(mods[1]); mods += 2;
				WORD font_id = (mods[0]<<8)|(mods[1]); mods += 2;
				WORD flags = mods[0]; mods += 1;
				WORD size = mods[0]; mods += 1;
				const AP4_Byte* color = mods; mods += 4;

				if(end > str_len) end = str_len;

				if(start < end)
				{
					CStringW s;

					s.Format(L"{\\1c%02x%02x%02x\\1a%02x}", color[2], color[1], color[0], 255 - color[3]);
					chars[start].pre += s;
					chars[end-1].post += font_color;

					s.Format(L"{\\fs%d}", size);
					chars[start].pre += s;
					chars[end-1].post += font_size;

					s.Format(L"{\\b%d\\i%d\\u%d}", (flags&1) ? 1 : 0, (flags&2) ? 1 : 0, (flags&4) ? 1 : 0);
					chars[start].pre += s; 
					chars[end-1].post += font_flags;
				}
			}
		}
		else if(tag == 'hclr')
		{
			hclr = mods;
		}
		else if(tag == 'hlit')
		{
			WORD start = (mods[0]<<8)|(mods[1]); mods += 2;
			WORD end = (mods[0]<<8)|(mods[1]); mods += 2;

			if(end > str_len) end = str_len;

			if(start < end)
			{
				CStringW s;

				s.Format(L"{\\3c%02x%02x%02x\\3a%02x}", hclr[2], hclr[1], hclr[0], 255 - hclr[3]);
				chars[start].pre += s;
				chars[end-1].post += font_color;

				chars[start].pre += L"{\\bord0.1}";
				chars[end-1].post += L"{\\bord}";
			}
		}
		else if(tag == 'blnk')
		{
			WORD start = (mods[0]<<8)|(mods[1]); mods += 2;
			WORD end = (mods[0]<<8)|(mods[1]); mods += 2;

			if(end > str_len) end = str_len;

			if(start < end)
			{
				// cheap...

				for(int i = 0, alpha = 255; i < durationms; i += 750, alpha = 255 - alpha)
				{
					CStringW s;
					s.Format(L"{\\t(%d, %d, \\alpha&H%02x&)}", i, i + 750, alpha);
					chars[start].pre += s;
				}

				chars[end-1].post += L"{\\alpha}";
			}
		}
		else if(tag == 'tbox')
		{
			rbox.top = (mods[0]<<8)|(mods[1]); mods += 2;
			rbox.left = (mods[0]<<8)|(mods[1]); mods += 2;
			rbox.bottom = (mods[0]<<8)|(mods[1]); mods += 2;
			rbox.right = (mods[0]<<8)|(mods[1]); mods += 2;
		}
		else if(tag == 'krok' && !(desc.DisplayFlags & 0x800))
		{
			DWORD start_time = (mods[0]<<24)|(mods[1]<<16)|(mods[2]<<8)|(mods[3]); mods += 4;
			WORD krok_count = (mods[0]<<8)|(mods[1]); mods += 2;

			while(krok_count-- > 0)
			{
				DWORD end_time = (mods[0]<<24)|(mods[1]<<16)|(mods[2]<<8)|(mods[3]); mods += 4;
				WORD start = (mods[0]<<8)|(mods[1]); mods += 2;
				WORD end = (mods[0]<<8)|(mods[1]); mods += 2;

				if(end > str_len) end = str_len;

				if(start < end)
				{
					CStringW s;

					s.Format(L"{\\kt%d\\kf%d}", start_time/10, (end_time - start_time)/10);
					chars[start].pre += s;
					s.Format(L"{\\1c%02x%02x%02x\\1a%02x}", hclr[2], hclr[1], hclr[0], 255 - hclr[3]);
					chars[start].pre += s;
					chars[end-1].post += L"{\\kt}" + font_color;
				}

				start_time = end_time;
			}
		}

		mods = next;
	}

	// continous karaoke

	if(desc.DisplayFlags & 0x800)
	{
		CStringW s;

		s.Format(L"{\\1c%02x%02x%02x\\1a%02x}", hclr[2], hclr[1], hclr[0], 255 - hclr[3]);
		str += s;

		int breaks = 0;

		for(int i = 0, j = 0; i <= str_len; i++)
		{
			if(chars[i].c == '\n' /*|| chars[i].c == ' '*/)
			{
				breaks++;
			}
		}

		if(str_len > breaks)
		{
			float dur = (float)max(durationms - 500, 0) / 10;

			for(int i = 0, j = 0; i <= str_len; i++)
			{
				if(i == str_len || chars[i].c == '\n' /*|| chars[i].c == ' '*/)
				{
					s.Format(L"{\\kf%d}", (int)(dur * (i - j) / (str_len - breaks)));
					chars[j].pre += s;
					j = i+1;
				}
			}
		}
	}

	//

	for(int i = 0; i < str_len; i++) 
	{
		str += chars[i].pre;
		str += chars[i].c;
		if(desc.DisplayFlags & 0x20000) str += L"\\N";
		str += chars[i].post;
	}

	delete [] chars;

	//

	if(rbox.IsRectEmpty()) rbox.SetRect(0, 0, framesize.cx, framesize.cy);
	rbox.OffsetRect(translation);

	CRect rbox2 = rbox;
	rbox2.DeflateRect(2, 2);

	CRect r(0,0,0,0);
	if(rbox2.Height() > 0) {r.top = rbox2.top; r.bottom = framesize.cy - rbox2.bottom;}
	if(rbox2.Width() > 0) {r.left = rbox2.left; r.right = framesize.cx - rbox2.right;}

	CStringW hdr;
	hdr.Format(L"0,0,Text,,%d,%d,%d,%d,,{\\clip(%d,%d,%d,%d)}", 
		r.left, r.right, r.top, r.bottom,
		rbox.left, rbox.top, rbox.right, rbox.bottom);

	//

	return hdr + str;
}

bool CMP4SplitterFilter::DemuxLoop()
{
	HRESULT hr = S_OK;

	AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie();

	while(SUCCEEDED(hr) && !CheckRequest(NULL))
	{
		CAtlMap<DWORD, trackpos>::CPair* pPairNext = NULL;
		REFERENCE_TIME rtNext = 0;

		POSITION pos = m_trackpos.GetStartPosition();
		while(pos)
		{
			CAtlMap<DWORD, trackpos>::CPair* pPair = m_trackpos.GetNext(pos);

			AP4_Track* track = movie->GetTrack(pPair->m_key);

			CBaseSplitterOutputPin* pPin = GetOutputPin((DWORD)track->GetId());
			if(!pPin->IsConnected()) continue;

			REFERENCE_TIME rt = (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * pPair->m_value.ts);

			if(pPair->m_value.index < track->GetSampleCount() && (!pPairNext || rt < rtNext))
			{
				pPairNext = pPair;
				rtNext = rt;
			}
		}

		if(!pPairNext) break;

		AP4_Track* track = movie->GetTrack(pPairNext->m_key);

		CBaseSplitterOutputPin* pPin = GetOutputPin((DWORD)track->GetId());

		AP4_Sample sample;
		AP4_DataBuffer data;

		if(pPin && pPin->IsConnected() && AP4_SUCCEEDED(track->ReadSample(pPairNext->m_value.index, sample, data)))
		{
			const CMediaType& mt = pPin->CurrentMediaType();

			CAutoPtr<Packet> p(new Packet());
			p->TrackNumber = (DWORD)track->GetId();
			p->rtStart = (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetCts());
			p->rtStop = p->rtStart + (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetDuration());
			p->bSyncPoint = TRUE;

			// FIXME: slow search & stss->m_Entries is private

			if(AP4_StssAtom* stss = dynamic_cast<AP4_StssAtom*>(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss")))
			{
				if(stss->m_Entries.ItemCount() > 0)
				{
					p->bSyncPoint = FALSE;

					AP4_Cardinal i = -1;
					while(++i < stss->m_Entries.ItemCount())
						if(stss->m_Entries[i]-1 == pPairNext->m_value.index)
							p->bSyncPoint = TRUE;
				}
			}

			//

			if(track->GetType() == AP4_Track::TYPE_AUDIO && data.GetDataSize() == 1)
			{
				WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.Format();

				int nBlockAlign = 1200;

				if(wfe->nBlockAlign > 1)
				{
					nBlockAlign = wfe->nBlockAlign;
					pPairNext->m_value.index -= pPairNext->m_value.index % wfe->nBlockAlign;
				}

				p->rtStop = p->rtStart;

				int fFirst = true;

				while(AP4_SUCCEEDED(track->ReadSample(pPairNext->m_value.index, sample, data)))
				{
					AP4_Size size = data.GetDataSize();
					const AP4_Byte* ptr = data.GetData();
					for(int i = 0; i < size; i++) p->Add(ptr[i]);

					if(fFirst) {p->rtStart = p->rtStop = (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetCts()); fFirst = false;}
					p->rtStop += (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetDuration());

					if(pPairNext->m_value.index+1 >= track->GetSampleCount() || p->GetCount() >= nBlockAlign)
						break;

					pPairNext->m_value.index++;
				}
			}
			else if(track->GetType() == AP4_Track::TYPE_TEXT)
			{
				CStringA dlgln_bkg, dlgln_plaintext;

				const AP4_Byte* ptr = data.GetData();
				AP4_Size avail = data.GetDataSize();

				if(avail > 2)
				{
					AP4_UI16 size = (ptr[0] << 8) | ptr[1];

					if(size <= avail-2)
					{
						CStringA str;

						if(size >= 2 && ptr[2] == 0xfe && ptr[3] == 0xff)
						{
							CStringW wstr = CStringW((LPCWSTR)&ptr[2], size/2);
							for(int i = 0; i < wstr.GetLength(); i++) wstr.SetAt(i, ((WORD)wstr[i] >> 8) | ((WORD)wstr[i] << 8));
							str = UTF16To8(wstr);
						}
						else
						{
							str = CStringA((LPCSTR)&ptr[2], size);
						}

						CStringA dlgln = str;

						if(mt.subtype == MEDIASUBTYPE_ASS2)
						{
							AP4_SampleDescription* desc = track->GetSampleDescription(sample.GetDescriptionIndex());

							dlgln = "0,0,Text,,0000,0000,0000,0000,," + str;
							dlgln_plaintext = str;

							CPoint translation(0, 0);
							if(AP4_TkhdAtom* tkhd = dynamic_cast<AP4_TkhdAtom*>(track->GetTrakAtom()->GetChild(AP4_ATOM_TYPE_TKHD)))
							{
								AP4_Float x, y;
								tkhd->GetTranslation(x, y);
								translation.SetPoint((int)x, (int)y);
							}

							if(AP4_UnknownSampleDescription* unknown_desc = dynamic_cast<AP4_UnknownSampleDescription*>(desc)) // TEMP
							{
								AP4_SampleEntry* sample_entry = unknown_desc->GetSampleEntry();

								if(AP4_TextSampleEntry* text = dynamic_cast<AP4_TextSampleEntry*>(sample_entry))
								{
									const AP4_TextSampleEntry::AP4_TextDescription& d = text->GetDescription();

									// TODO
								}
								else if(AP4_Tx3gSampleEntry* tx3g = dynamic_cast<AP4_Tx3gSampleEntry*>(sample_entry))
								{
									const AP4_Tx3gSampleEntry::AP4_Tx3gDescription& desc = tx3g->GetDescription();

									CStringW font = L"Arial";

									if(AP4_FtabAtom* ftab = dynamic_cast<AP4_FtabAtom*>(tx3g->GetChild(AP4_ATOM_TYPE_FTAB)))
									{
										AP4_String Name;
										if(AP4_SUCCEEDED(ftab->LookupFont(desc.Style.Font.Id, Name)))
											font = Name.c_str();
									}					

									CRect rbox;
									CStringW ssa = ConvertTX3GToSSA(
										UTF8To16(str), desc, font,
										ptr + (2 + size), avail - (2 + size), 
										m_framesize, translation, 
										(p->rtStop - p->rtStart)/10000,
										rbox);
									dlgln = UTF16To8(ssa);

									const AP4_Byte* bclr = (const AP4_Byte*)&desc.BackgroundColor;

									if(bclr[3])
									{
										CPoint tl = rbox.TopLeft();
										rbox.OffsetRect(-tl.x, -tl.y);

										dlgln_bkg.Format(
											"0,-1,Text,,0,0,0,0,,{\\an7\\pos(%d,%d)\\1c%02x%02x%02x\\1a%02x\\bord0\\shad0}{\\p1}m %d %d l %d %d l %d %d l %d %d {\\p0}", 
											tl.x, tl.y, 
											bclr[2], bclr[1], bclr[0], 
											255 - bclr[3],
											rbox.left, rbox.top, 
											rbox.right, rbox.top,
											rbox.right, rbox.bottom,
											rbox.left, rbox.bottom);
									}
								}
							}
						}

						dlgln.Replace("\r", "");
						dlgln.Replace("\n", "\\N");

						p->SetData((LPCSTR)dlgln, dlgln.GetLength());
					}
				}

				if(!dlgln_bkg.IsEmpty())
				{
					CAutoPtr<Packet> p2(new Packet());
					p2->TrackNumber = p->TrackNumber;
					p2->rtStart = p->rtStart;
					p2->rtStop = p->rtStop;
					p2->bSyncPoint = p->bSyncPoint;
					p2->SetData((LPCSTR)dlgln_bkg, dlgln_bkg.GetLength());
					hr = DeliverPacket(p2);
				}

				if(!dlgln_plaintext.IsEmpty())
				{
					CAutoPtr<Packet> p2(new Packet());
					p2->TrackNumber = p->TrackNumber ^ 0x80402010;
					p2->rtStart = p->rtStart;
					p2->rtStop = p->rtStop;
					p2->bSyncPoint = p->bSyncPoint;
					p2->SetData((LPCSTR)dlgln_plaintext, dlgln_plaintext.GetLength());
					hr = DeliverPacket(p2);
				}
			}
			else
			{
				p->SetData(data.GetData(), data.GetDataSize());
			}

			hr = DeliverPacket(p);
		}

		{
			AP4_Sample sample;
			if(AP4_SUCCEEDED(track->GetSample(++pPairNext->m_value.index, sample)))
				pPairNext->m_value.ts = sample.GetCts();
		}

	}

	return true;
}

// IKeyFrameInfo

STDMETHODIMP CMP4SplitterFilter::GetKeyFrameCount(UINT& nKFs)
{
	CheckPointer(m_pFile, E_UNEXPECTED);

	if(!m_pFile) return E_UNEXPECTED;

	AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie();

	POSITION pos = m_trackpos.GetStartPosition();
	while(pos)
	{
		CAtlMap<DWORD, trackpos>::CPair* pPair = m_trackpos.GetNext(pos);

		AP4_Track* track = movie->GetTrack(pPair->m_key);

		if(track->GetType() != AP4_Track::TYPE_VIDEO)
			continue;

		if(AP4_StssAtom* stss = dynamic_cast<AP4_StssAtom*>(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss")))
		{
			nKFs = stss->m_Entries.ItemCount();
			return S_OK;
		}
	}

	return E_FAIL;
}

STDMETHODIMP CMP4SplitterFilter::GetKeyFrames(const GUID* pFormat, REFERENCE_TIME* pKFs, UINT& nKFs)
{
	CheckPointer(pFormat, E_POINTER);
	CheckPointer(pKFs, E_POINTER);
	CheckPointer(m_pFile, E_UNEXPECTED);

	if(*pFormat != TIME_FORMAT_MEDIA_TIME) return E_INVALIDARG;

	if(!m_pFile) return E_UNEXPECTED;

	AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie();

	POSITION pos = m_trackpos.GetStartPosition();
	while(pos)
	{
		CAtlMap<DWORD, trackpos>::CPair* pPair = m_trackpos.GetNext(pos);

		AP4_Track* track = movie->GetTrack(pPair->m_key);

		if(track->GetType() != AP4_Track::TYPE_VIDEO)
			continue;

		if(AP4_StssAtom* stss = dynamic_cast<AP4_StssAtom*>(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss")))
		{
			nKFs = 0;

			for(AP4_Cardinal i = 0; i < stss->m_Entries.ItemCount(); i++)
			{
				AP4_Sample sample;
				if(AP4_SUCCEEDED(track->GetSample(stss->m_Entries[i]-1, sample)))
					pKFs[nKFs++] = 10000000i64 * sample.GetCts() / track->GetMediaTimeScale();
			}

			return S_OK;
		}
	}

	return E_FAIL;
}

//
// CMP4SourceFilter
//

CMP4SourceFilter::CMP4SourceFilter(LPUNKNOWN pUnk, HRESULT* phr)
	: CMP4SplitterFilter(pUnk, phr)
{
	m_clsid = __uuidof(this);
	m_pInput.Free();
}

//
// CMPEG4VideoSplitterFilter
//

CMPEG4VideoSplitterFilter::CMPEG4VideoSplitterFilter(LPUNKNOWN pUnk, HRESULT* phr)
	: CBaseSplitterFilter(NAME("CMPEG4VideoSplitterFilter"), pUnk, phr, __uuidof(this))
{
}

void CMPEG4VideoSplitterFilter::SkipUserData()
{
	m_pFile->BitByteAlign();
	while(m_pFile->BitRead(32, true) == 0x000001b2)
		while(m_pFile->BitRead(24, true) != 0x000001)
			m_pFile->BitRead(8);
}

HRESULT CMPEG4VideoSplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader)
{
	CheckPointer(pAsyncReader, E_POINTER);

	HRESULT hr = E_FAIL;

	m_pFile.Free();
	m_pFile.Attach(new CBaseSplitterFileEx(pAsyncReader, hr));
	if(!m_pFile) return E_OUTOFMEMORY;
	if(FAILED(hr)) {m_pFile.Free(); return hr;}

	m_rtNewStart = m_rtCurrent = 0;
	m_rtNewStop = m_rtStop = m_rtDuration = 0;

	// TODO

	DWORD width = 0;
	DWORD height = 0;
	BYTE parx = 1;
	BYTE pary = 1;
	REFERENCE_TIME atpf = 400000;

	if(m_pFile->BitRead(24, true) != 0x000001)
		return E_FAIL;

	BYTE id;
	while(m_pFile->NextMpegStartCode(id, 1024 - m_pFile->GetPos()))
	{
		if(id == 0xb5)
		{
			BYTE is_visual_object_identifier = (BYTE)m_pFile->BitRead(1);

			if(is_visual_object_identifier)
			{
				BYTE visual_object_verid = (BYTE)m_pFile->BitRead(4);
				BYTE visual_object_priority = (BYTE)m_pFile->BitRead(3);
			}

			BYTE visual_object_type = (BYTE)m_pFile->BitRead(4);
			
			if(visual_object_type == 1 || visual_object_type == 2)
			{
				BYTE video_signal_type = (BYTE)m_pFile->BitRead(1);
				
				if(video_signal_type)
				{
					BYTE video_format = (BYTE)m_pFile->BitRead(3);
					BYTE video_range = (BYTE)m_pFile->BitRead(1);
					BYTE colour_description = (BYTE)m_pFile->BitRead(1);

					if(colour_description)
					{
						BYTE colour_primaries = (BYTE)m_pFile->BitRead(8);
						BYTE transfer_characteristics = (BYTE)m_pFile->BitRead(8);
						BYTE matrix_coefficients = (BYTE)m_pFile->BitRead(8);
					}
				}
			}

			SkipUserData();

			if(visual_object_type == 1)
			{
				if(m_pFile->BitRead(24) != 0x000001)
					break;

				BYTE video_object_start_code = (BYTE)m_pFile->BitRead(8);
				if(video_object_start_code < 0x00 || video_object_start_code > 0x1f)
					break;

				if(m_pFile->BitRead(24) != 0x000001)
					break;

				BYTE video_object_layer_start_code = (DWORD)m_pFile->BitRead(8);
				if(video_object_layer_start_code < 0x20 || video_object_layer_start_code > 0x2f)
					break;

				BYTE random_accessible_vol = (BYTE)m_pFile->BitRead(1);
				BYTE video_object_type_indication = (BYTE)m_pFile->BitRead(8);

				if(video_object_type_indication == 0x12) // Fine Granularity Scalable
					break; // huh

				BYTE is_object_layer_identifier = (BYTE)m_pFile->BitRead(1);

				BYTE video_object_layer_verid = 0;

				if(is_object_layer_identifier)
				{
					video_object_layer_verid = (BYTE)m_pFile->BitRead(4);
					BYTE video_object_layer_priority = (BYTE)m_pFile->BitRead(3);
				}

				BYTE aspect_ratio_info = (BYTE)m_pFile->BitRead(4);

				switch(aspect_ratio_info)
				{
				default: ASSERT(0); break;
				case 1: parx = 1; pary = 1; break;
				case 2: parx = 12; pary = 11; break;
				case 3: parx = 10; pary = 11; break;
				case 4: parx = 16; pary = 11; break;
				case 5: parx = 40; pary = 33; break;
				case 15: parx = (BYTE)m_pFile->BitRead(8); pary = (BYTE)m_pFile->BitRead(8); break;
				}

				BYTE vol_control_parameters = (BYTE)m_pFile->BitRead(1);

				if(vol_control_parameters)
				{
					BYTE chroma_format = (BYTE)m_pFile->BitRead(2);
					BYTE low_delay = (BYTE)m_pFile->BitRead(1);
					BYTE vbv_parameters = (BYTE)m_pFile->BitRead(1);

					if(vbv_parameters)
					{
						WORD first_half_bit_rate = (WORD)m_pFile->BitRead(15);
						if(!m_pFile->BitRead(1)) break;
						WORD latter_half_bit_rate = (WORD)m_pFile->BitRead(15);
						if(!m_pFile->BitRead(1)) break;
						WORD first_half_vbv_buffer_size = (WORD)m_pFile->BitRead(15);
						if(!m_pFile->BitRead(1)) break;

						BYTE latter_half_vbv_buffer_size = (BYTE)m_pFile->BitRead(3);
						WORD first_half_vbv_occupancy = (WORD)m_pFile->BitRead(11);
						if(!m_pFile->BitRead(1)) break;
						WORD latter_half_vbv_occupancy = (WORD)m_pFile->BitRead(15);
						if(!m_pFile->BitRead(1)) break;
					}
				}

				BYTE video_object_layer_shape = (BYTE)m_pFile->BitRead(2);

				if(video_object_layer_shape == 3 && video_object_layer_verid != 1)
				{
					BYTE video_object_layer_shape_extension = (BYTE)m_pFile->BitRead(4);
				}

				if(!m_pFile->BitRead(1)) break;
				WORD vop_time_increment_resolution = (WORD)m_pFile->BitRead(16);
				if(!m_pFile->BitRead(1)) break;
				BYTE fixed_vop_rate = (BYTE)m_pFile->BitRead(1);

				if(fixed_vop_rate)
				{
					int bits = 0;
					for(WORD i = vop_time_increment_resolution; i; i /= 2)
						bits++;

					WORD fixed_vop_time_increment = m_pFile->BitRead(bits);

					if(fixed_vop_time_increment)
					{
						atpf = 10000000i64 * fixed_vop_time_increment / vop_time_increment_resolution;
					}
				}

				if(video_object_layer_shape != 2)
				{
					if(video_object_layer_shape == 0)
					{
						if(!m_pFile->BitRead(1)) break;
						width = (WORD)m_pFile->BitRead(13);
						if(!m_pFile->BitRead(1)) break;
						height = (WORD)m_pFile->BitRead(13);
						if(!m_pFile->BitRead(1)) break;
					}

					BYTE interlaced = (BYTE)m_pFile->BitRead(1);
					BYTE obmc_disable = (BYTE)m_pFile->BitRead(1);

					// ...
				}
			}
		}
		else if(id == 0xb6)
		{
			m_seqhdrsize = m_pFile->GetPos() - 4;			
		}
	}

	if(!width || !height) 
		return E_FAIL;

	CAtlArray<CMediaType> mts;

	CMediaType mt;
	mt.SetSampleSize(1);

	mt.majortype = MEDIATYPE_Video;
	mt.subtype = FOURCCMap('v4pm');
	mt.formattype = FORMAT_MPEG2Video;
	MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + m_seqhdrsize);
	memset(vih, 0, mt.FormatLength());
	vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader);
	vih->hdr.bmiHeader.biWidth = width;
	vih->hdr.bmiHeader.biHeight = height;
	vih->hdr.bmiHeader.biCompression = 'v4pm';
	vih->hdr.bmiHeader.biPlanes = 1;
	vih->hdr.bmiHeader.biBitCount = 24;
	vih->hdr.AvgTimePerFrame = atpf;
	vih->hdr.dwPictAspectRatioX = width*parx;
	vih->hdr.dwPictAspectRatioY = height*pary;
	vih->cbSequenceHeader = m_seqhdrsize;
	m_pFile->Seek(0);
	m_pFile->ByteRead((BYTE*)vih->dwSequenceHeader, m_seqhdrsize);
	mts.Add(mt);
	mt.subtype = FOURCCMap(vih->hdr.bmiHeader.biCompression = 'V4PM');
	mts.Add(mt);

	CAutoPtr<CBaseSplitterOutputPin> pPinOut(new CBaseSplitterOutputPin(mts, L"Video", this, this, &hr));
	EXECUTE_ASSERT(SUCCEEDED(AddOutputPin(0, pPinOut)));

	m_rtNewStop = m_rtStop = m_rtDuration;

	return m_pOutputs.GetCount() > 0 ? S_OK : E_FAIL;
}

bool CMPEG4VideoSplitterFilter::DemuxInit()
{
	return true;
}

void CMPEG4VideoSplitterFilter::DemuxSeek(REFERENCE_TIME rt)
{
	ASSERT(rt == 0);

	m_pFile->Seek(m_seqhdrsize);
}

bool CMPEG4VideoSplitterFilter::DemuxLoop()
{
	HRESULT hr = S_OK;

	CAutoPtr<Packet> p;

	REFERENCE_TIME rt = 0;
	REFERENCE_TIME atpf = ((MPEG2VIDEOINFO*)GetOutputPin(0)->CurrentMediaType().Format())->hdr.AvgTimePerFrame;

	DWORD sync = ~0;

	while(SUCCEEDED(hr) && !CheckRequest(NULL) && m_pFile->GetRemaining())
	{
		for(int i = 0; i < 65536; i++) // don't call CheckRequest so often
		{
			bool eof = !m_pFile->GetRemaining();

			if(p && !p->IsEmpty() && (m_pFile->BitRead(32, true) == 0x000001b6 || eof))
			{
				hr = DeliverPacket(p);
			}

			if(eof) break;

			if(!p)
			{
				p.Attach(new Packet());
				p->SetCount(0, 1024);
				p->TrackNumber = 0;
				p->rtStart = rt; 
				p->rtStop = rt + atpf;
				p->bSyncPoint = FALSE;
				rt += atpf; 
				// rt = Packet::INVALID_TIME;
			}

			BYTE b;
			m_pFile->ByteRead(&b, 1);
			p->Add(b);
		}
	}

	return true;
}

//
// CMPEG4VideoSourceFilter
//

CMPEG4VideoSourceFilter::CMPEG4VideoSourceFilter(LPUNKNOWN pUnk, HRESULT* phr)
	: CMPEG4VideoSplitterFilter(pUnk, phr)
{
	m_clsid = __uuidof(this);
	m_pInput.Free();
}




See more files for this project here

guliverkli

Home of VobSub, Media Player Classic (MPC) and other misc utils.

Project homepage: http://sourceforge.net/projects/guliverkli
Programming language(s): C,C++,PHP
License: other

  AP4/
    Update/
      v0.6.3/
        Source/
          Config/
            Ap4Config.h
          Core/
            Ap4Atom.h
            Ap4AtomFactory.cpp
            Ap4AtomFactory.h
            Ap4AtomSampleTable.cpp
            Ap4AtomSampleTable.h
            Ap4AvcCAtom.cpp
            Ap4AvcCAtom.h
            Ap4ByteStream.cpp
            Ap4ByteStream.h
            Ap4ChplAtom.cpp
            Ap4ChplAtom.h
            Ap4CmvdAtom.cpp
            Ap4CmvdAtom.h
            Ap4Co64Atom.cpp
            Ap4Co64Atom.h
            Ap4ContainerAtom.cpp
            Ap4CttsAtom.cpp
            Ap4CttsAtom.h
            Ap4DataAtom.cpp
            Ap4DataAtom.h
            Ap4DcomAtom.cpp
            Ap4DcomAtom.h
            Ap4DrefAtom.cpp
            Ap4File.cpp
            Ap4FtabAtom.cpp
            Ap4FtabAtom.h
            Ap4HdlrAtom.cpp
            Ap4HdlrAtom.h
            Ap4MdhdAtom.cpp
            Ap4MdhdAtom.h
            Ap4MoovAtom.cpp
            Ap4Movie.cpp
            Ap4Movie.h
            Ap4MvhdAtom.cpp
            Ap4MvhdAtom.h
            Ap4Sample.cpp
            Ap4Sample.h
            Ap4SampleDescription.h
            Ap4SampleEntry.cpp
            Ap4SampleEntry.h
            Ap4StcoAtom.cpp
            Ap4StscAtom.h
            Ap4StsdAtom.cpp
            Ap4StsdAtom.h
            Ap4StssAtom.h
            Ap4StszAtom.cpp
            Ap4StszAtom.h
            Ap4SttsAtom.cpp
            Ap4SttsAtom.h
            Ap4TkhdAtom.cpp
            Ap4TkhdAtom.h
            Ap4Track.cpp
            Ap4Track.h
            Ap4TrakAtom.cpp
            Ap4TrakAtom.h
            Ap4Types.h
            Ap4Utils.cpp
            Ap4Utils.h
          Crypto/
            Ap4AesBlockCipher.cpp
        Bento4_0.6.3-001.7z
  Ap4AsyncReaderStream.cpp
  Ap4AsyncReaderStream.h
  MP4Splitter.cpp
  MP4Splitter.def
  MP4Splitter.h
  MP4Splitter.rc
  MP4SplitterFile.cpp
  MP4SplitterFile.h
  MP4Splitter_vs2005.sln
  MP4Splitter_vs2005.vcproj
  readme.txt
  resource.h
  stdafx.cpp
  stdafx.h