//---------------------------------------------------------------------------
//	AVFile - library for audio and video files processing
//	2002 - 2007 Stanislav Sumec <sumec@fit.vutbr.cz>
//
//	dsfile.cpp
//	DirectShow file reader
//
//	10.2.2004
//		- first release
//---------------------------------------------------------------------------

#include "dshow_hack.h"
#include "BaseClasses_hack.h"
#include "dsfile.h"
#include "dsfilei.h"
#include "SampleGrabberCB.h"
#include <queue>

//---------------------------------------------------------------------------
TDSFile::TDSFile()
{
	RegisterFileType("avi");
	RegisterFileType("mpg");
	RegisterFileType("mpeg");
	RegisterFileType("wav");
	RegisterFileType("mp3");
	Description = "DirectShow file input";
	Implemented = iGetFrameB + iGetFrameB2 + iDIBGetFrameB;
	Capabilities = cDependentStreams;
	Internal = new TDSFileI();
	CoInitialized = false;
	Bitmap = NULL;
	FrameBuffer = NULL;
	AudioBuffer = NULL;
	Internal->VideoCB = Internal->AudioCB = NULL;
	Internal->g_dwGraphRegister = Internal->g_dwAudioGraphRegister = 0;
}

//---------------------------------------------------------------------------
TDSFile::~TDSFile()
{
	Close();
	delete Internal;
}

//---------------------------------------------------------------------------
void TDSFile::OpenA(const char *FileName, int Mode, int Type, bool ThrowExceptions)
{
	USES_CONVERSION;
	HRESULT hr;
	Close();

	if (Mode == omRead)
	{
		if ((Type & otVideo) || (Type & otAudio))
		{
			char *Error = NULL;

			if (!CoInitialized)
			{
				if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
				{
					Error = "Could not CoInitializeEx!";
				}
				else
				{
					CoInitialized = true;
				}
			}

			if (Error == NULL)
			{
				Internal->pGraph.CoCreateInstance(CLSID_FilterGraph);
				if (!Internal->pGraph)
				{
					Error = "Could not create the FilterGraph!";
				}
			}

			if (Error == NULL)
			{
				Internal->pBilder.CoCreateInstance(CLSID_CaptureGraphBuilder2);
				if (!Internal->pBilder)
				{
					Error = "Could not create the FilterGraph!";
				}
				else
				{
					hr = Internal->pBilder->SetFiltergraph(Internal->pGraph);
					if (hr != S_OK)
					{
						Error = "Could not set FilterGraph!";
					}
				}
			}

			if (Error == NULL)
			{
				Internal->g_dwGraphRegister = 0;
				Internal->AddGraphToRot(Internal->pGraph, &Internal->g_dwGraphRegister);
			}

			if (Error == NULL)
			{
				Internal->pControl = Internal->pGraph;
				if (!Internal->pControl)
				{
					Error = "Could not create the MediaControl!";
				}
			}

			if (Error == NULL)
			{
				Internal->pSeeking = Internal->pGraph;
				if (!Internal->pSeeking)
				{
					Error = "Could not create the MediaSeeking!";
				}
			}

			if (Error == NULL)
			{
				Internal->pMediaEvent = Internal->pGraph;
				if (!Internal->pMediaEvent)
				{
					Error = "Could not create the MediaEvent!";
				}
			}

			CComPtr<IBaseFilter> pSource;
			if (Error == NULL)
			{
				pSource.CoCreateInstance(CLSID_AsyncReader);
				if (!pSource)
				{
					Error = "Could not create AsyncReader!";
				}
			}

			if (Error == NULL)
			{
				hr = Internal->pGraph->AddFilter(pSource, L"Source");
				if (FAILED(hr))
				{
					Error = "Could not add filter!";
				}
			}

			if (Error == NULL)
			{
				CComQIPtr<IFileSourceFilter, &IID_IFileSourceFilter> pLoad(pSource);
				hr = pLoad->Load(T2W(FileName), NULL);
				if (FAILED(hr))
				{
					Error = "Could not load the media file!";
				}
			}

			if (Error == NULL)
			{
				if (Type & otVideo)
				{
					Internal->pVideoGrabber.CoCreateInstance(CLSID_SampleGrabber);
					if (!Internal->pVideoGrabber)
					{
						Error = "Could not create SampleGrabber!";
					}

					CComQIPtr<IBaseFilter, &IID_IBaseFilter> pGrabberBase(Internal->pVideoGrabber);
					if (!pGrabberBase)
					{
						Error = "Could not create IBaseFilter for SampleGrabber!";
					}

					CComPtr<IBaseFilter> pDeinterlace;
					if (Error == NULL)
					{
						pDeinterlace.CoCreateInstance(CLSID_Deinterlace);
					}

					if (Error == NULL)
					{
						hr = Internal->pGraph->AddFilter(pGrabberBase, L"Grabber");
						if (FAILED(hr))
						{
							Error = "Could not add filter!";
						}
					}

					if (Error == NULL)
					{
						if (pDeinterlace)
						{
							hr = Internal->pGraph->AddFilter(pDeinterlace, L"Deinterlace");
							if (FAILED(hr))
							{
								Error = "Could not add filter!";
							}
						}
					}

					if (Error == NULL)
					{
						// Tell the grabber to grab 24-bit video. Must do this
						// before connecting it
						CMediaType GrabType;
						GrabType.SetType(&MEDIATYPE_Video);
						GrabType.SetSubtype(&MEDIASUBTYPE_RGB32);
						hr = Internal->pVideoGrabber->SetMediaType(&GrabType);
						if (FAILED(hr))
						{
							Error = "Could not set media type!";
						}
					}

					if (Error == NULL)
					{
						if (pDeinterlace)
						{
							hr = Internal->pBilder->RenderStream(NULL, NULL, pSource, NULL, pDeinterlace);
							if (FAILED(hr))
							{
								Error = "Could not connect source filter to grabber!";
							}
							else
							{
								hr = Internal->pBilder->RenderStream(NULL, NULL, pDeinterlace, NULL, pGrabberBase);
								if (FAILED(hr))
								{
									Error = "Could not connect source filter to grabber!";
								}
							}
						}
						else
						{
							hr = Internal->pBilder->RenderStream(NULL, NULL, pSource, NULL, pGrabberBase);
							if (FAILED(hr))
							{
								Error = "Could not connect source filter to grabber!";
							}
						}
					}

					if (Error == NULL)
					{
						AM_MEDIA_TYPE mt;
						hr = Internal->pVideoGrabber->GetConnectedMediaType(&mt);
						if (FAILED(hr))
						{
							Error = "Could not get media type!";
						}
						else
						{
							VIDEOINFOHEADER *vih = (VIDEOINFOHEADER*)mt.pbFormat;

							VideoInfo.Width = vih->bmiHeader.biWidth;
							VideoInfo.Height = vih->bmiHeader.biHeight;
							VideoInfo.FrameRate.Rate = 25;
							VideoInfo.FrameRate.Scale = 1;

							ScanLineWidth = VideoInfo.Width * 4;
							AlignedScanLineWidth = DIB_SCAN_LINE(ScanLineWidth);

							BitmapDataSize = VideoInfo.Height * AlignedScanLineWidth;
							Bitmap = (BITMAPINFO*)GetMem(sizeof(BITMAPINFOHEADER) + BitmapDataSize);
							if (Bitmap == NULL)
							{
								Error = "Could not allocate memory!";
							}
							else
							{
								BitmapData = (BYTE*)Bitmap + sizeof(BITMAPINFOHEADER);
								Bitmap->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
								Bitmap->bmiHeader.biWidth = VideoInfo.Width;
								Bitmap->bmiHeader.biHeight = VideoInfo.Height;

								Bitmap->bmiHeader.biPlanes = 1;
								Bitmap->bmiHeader.biBitCount = 32;
								Bitmap->bmiHeader.biCompression = BI_RGB;
								Bitmap->bmiHeader.biSizeImage = BitmapDataSize;
								Bitmap->bmiHeader.biXPelsPerMeter = 0;
								Bitmap->bmiHeader.biYPelsPerMeter = 0;
								Bitmap->bmiHeader.biClrUsed = 0;
								Bitmap->bmiHeader.biClrImportant = 0;
							}
							FreeMediaType(mt);
						}
					}

					if (Error == NULL)
					{
						FrameBuffer = (BYTE*)GetMem(VideoInfo.Width * 4 * VideoInfo.Height);
						if (FrameBuffer == NULL)
						{
							Error = "Could not allocate memory!";
						}
					}

					CComPtr<IBaseFilter> pNullRenderer;
					if (Error == NULL)
					{
						pNullRenderer.CoCreateInstance(CLSID_NullRenderer);
						if (!pNullRenderer)
						{
							Error = "Could not create NullRenderer!";
						}
					}

					if (Error == NULL)
					{
						hr = Internal->pGraph->AddFilter(pNullRenderer, L"NullRenderer");
						if (FAILED(hr))
						{
							Error = "Could not add filter!";
						}
					}

					if (Error == NULL)
					{
						hr = Internal->pBilder->RenderStream(NULL, NULL, pGrabberBase, NULL, pNullRenderer);
						if (FAILED(hr))
						{
							Error = "Could not connect null renderer!";
						}
					}

					if (Error == NULL)
					{
						LONGLONG MediaTimeDuration;
						bool MediaTimeDurationSet = false;
						hr = Internal->pSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
						if (!FAILED(hr))
						{
							hr = Internal->pSeeking->GetDuration(&MediaTimeDuration);
							MediaTimeDurationSet = !FAILED(hr);
						}

						hr = Internal->pSeeking->SetTimeFormat(&TIME_FORMAT_FRAME);
						if (FAILED(hr))
						{
							Error = "Could not set time format!";
						}

						if (Error == NULL)
						{
							LONGLONG Duration;
							hr = Internal->pSeeking->GetDuration(&Duration);
							if (FAILED(hr))
							{
								Error = "Could not get duration!";
							}
							else
							{
								Length = Duration;
								if (MediaTimeDurationSet)
								{
									VideoInfo.FrameRate.Rate = (LONGLONG(10000000000) * Duration) / MediaTimeDuration;
									VideoInfo.FrameRate.Scale = 1000;
								}
							}
						}
					}

					if (Error == NULL)
					{
						if (FAILED(Internal->pVideoGrabber->SetBufferSamples(FALSE)) || FAILED(Internal->pVideoGrabber->SetOneShot(FALSE)))
						{
							Error = "Could not setup grabber!";
						}
					}

					if (Error == NULL)
					{
						CComQIPtr<IMediaFilter, &IID_IMediaFilter> pMediaFilter(Internal->pGraph);
						if (pMediaFilter)
						{
							hr = pMediaFilter->SetSyncSource(NULL);
						}
					}

					if (Error == NULL)
					{
						Internal->VideoCB = new TSampleGrabberCB();
						hr = Internal->pVideoGrabber->SetCallback(Internal->VideoCB, 1);
						if (FAILED(hr))
						{
							Error = "Could not setup callback!";
						}
					}

					if (Error != NULL)
					{
						if (ThrowExceptions)
						{
							Close();
							throw AVFileErrorOpenVideo(Error);
						}
						Error = NULL;
					}
					else
					{
						FrameInBuffer = ~0;
						VideoPos = 0;
						OpenMode = Mode;
						OpenType |= otVideo;
					}
				}

				if (Type & otAudio)
				{
					Internal->pAudioGrabber.CoCreateInstance(CLSID_SampleGrabber);
					if (!Internal->pAudioGrabber)
					{
						Error = "Could not create SampleGrabber!";
					}

					CComQIPtr<IBaseFilter, &IID_IBaseFilter> pGrabberBase(Internal->pAudioGrabber);
					if (!pGrabberBase)
					{
						Error = "Could not create IBaseFilter for SampleGrabber!";
					}

					if (Error == NULL)
					{
						hr = Internal->pGraph->AddFilter(pGrabberBase, L"Grabber");
						if (FAILED(hr))
						{
							Error = "Could not add filter!";
						}
					}

					if (Error == NULL)
					{
						CMediaType GrabType;
						GrabType.SetType(&MEDIATYPE_Audio);
						GrabType.SetSubtype(&MEDIASUBTYPE_PCM);
						hr = Internal->pAudioGrabber->SetMediaType(&GrabType);
						if (FAILED(hr))
						{
							Error = "Could not set media type!";
						}
					}

					if (Error == NULL)
					{
						hr = Internal->pBilder->RenderStream(NULL, NULL, pSource, NULL, pGrabberBase);
						if (FAILED(hr))
						{
							Error = "Could not connect source filter to grabber!";
						}
					}

					if (Error == NULL)
					{
						AM_MEDIA_TYPE mt;
						hr = Internal->pAudioGrabber->GetConnectedMediaType(&mt);
						if (FAILED(hr))
						{
							Error = "Could not get media type!";
						}
						else
						{
							WAVEFORMATEX *wfx = (WAVEFORMATEX*)mt.pbFormat;
							if (wfx->wFormatTag == WAVE_FORMAT_PCM && wfx->wBitsPerSample == 16 && mt.bFixedSizeSamples)
							{
								AudioInfo.Channels = wfx->nChannels;
								AudioInfo.SamplesPerSec = wfx->nSamplesPerSec;
							}
							else
							{
								Error = "Invalid audio format!";
							}
							FreeMediaType(mt);
						}
					}

					CComPtr<IBaseFilter> pNullRenderer;
					if (Error == NULL)
					{
						pNullRenderer.CoCreateInstance(CLSID_NullRenderer);
						if (!pNullRenderer)
						{
							Error = "Could not create NullRenderer!";
						}
					}

					if (Error == NULL)
					{
						hr = Internal->pGraph->AddFilter(pNullRenderer, L"NullRenderer");
						if (FAILED(hr))
						{
							Error = "Could not add filter!";
						}
					}

					if (Error == NULL)
					{
						hr = Internal->pBilder->RenderStream(NULL, NULL, pGrabberBase, NULL, pNullRenderer);
						if (FAILED(hr))
						{
							Error = "Could not connect null renderer!";
						}
					}

					if (Error == NULL)
					{
						if (FAILED(Internal->pAudioGrabber->SetBufferSamples(FALSE)) || FAILED(Internal->pAudioGrabber->SetOneShot(FALSE)))
						{
							Error = "Could not setup grabber!";
						}
					}

					if (Error == NULL)
					{
						hr = Internal->pSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
						if (FAILED(hr))
						{
							Error = "Could not set time format!";
						}

						if (Error == NULL)
						{
							LONGLONG Duration;
							hr = Internal->pSeeking->GetDuration(&Duration);
							if (FAILED(hr))
							{
								Error = "Could not get duration!";
							}

							AudioLength = (LONGLONG(AudioInfo.SamplesPerSec) * Duration) / LONGLONG(10000000);
						}
					}

					if (Error == NULL)
					{
						CComQIPtr<IMediaFilter, &IID_IMediaFilter> pMediaFilter(Internal->pGraph);
						if (pMediaFilter)
						{
							hr = pMediaFilter->SetSyncSource(NULL);
						}
					}

					if (Error == NULL)
					{
						Internal->AudioCB = new TSampleGrabberCB();
						hr = Internal->pAudioGrabber->SetCallback(Internal->AudioCB, 1);
						if (FAILED(hr))
						{
							Error = "Could not setup callback!";
						}
					}

					if (Error != NULL)
					{
						if (ThrowExceptions)
						{
							Close();
							throw AVFileErrorOpenVideo(Error);
						}
					}
					else
					{
						AudioPos = 0;
						OpenMode = Mode;
						OpenType |= otAudio;
					}
				}
			}
			else
			{
				if (ThrowExceptions)
				{
					Close();
					throw AVFileErrorOpenVideo(Error);
				}
			}
		}
	}

	if (ThrowExceptions)
	{
		if (Mode != OpenMode)
		{
			Close();
			throw AVFileErrorOpenInvalidMode();
		}
		if ((Type & otVideo) && !(OpenType & otVideo))
		{
			Close();
			throw AVFileErrorOpenVideo();
		}
		if ((Type & otAudio) && !(OpenType & otAudio))
		{
			Close();
			throw AVFileErrorOpenAudio();
		}
	}
}

//---------------------------------------------------------------------------
void TDSFile::Close()
{
	OpenMode = omClosed;
	OpenType = otNone;

	if (Internal->pControl)
	{
		Internal->pControl.Release();
	}

	if (Internal->pSeeking)
	{
		Internal->pSeeking.Release();
	}

	if (Internal->pMediaEvent)
	{
		Internal->pMediaEvent.Release();
	}

	if (Internal->pBilder)
	{
		Internal->pBilder.Release();
	}

	if (Internal->pGraph)
	{
		if (Internal->g_dwGraphRegister != 0)
		{
			Internal->RemoveGraphFromRot(Internal->g_dwGraphRegister);
			Internal->g_dwGraphRegister = 0;
		}
		Internal->pGraph.Release();
	}

	if (Internal->pVideoGrabber)
	{
		Internal->pVideoGrabber.Release();
	}

	if (Internal->pAudioGrabber)
	{
		Internal->pAudioGrabber.Release();
	}

	if (Internal->VideoCB != NULL)
	{
		delete Internal->VideoCB;
		Internal->VideoCB = NULL;
	}

	if (Internal->AudioCB != NULL)
	{
		delete Internal->AudioCB;
		Internal->AudioCB = NULL;
	}

	if (CoInitialized)
	{
		CoUninitialize();
		CoInitialized = false;
	}

	if (Bitmap != NULL)
	{
		FreeMem(Bitmap);
		Bitmap = NULL;
	}

	if (FrameBuffer != NULL)
	{
		FreeMem(FrameBuffer);
		FrameBuffer = NULL;
	}

	if (AudioBuffer != NULL)
	{
		FreeMem(AudioBuffer);
		AudioBuffer = NULL;
	}
}

//---------------------------------------------------------------------------
unsigned int TDSFile::GetVideoLength()
{
	CheckOpened();
	CheckVideoStream();
	return Length;
}

//---------------------------------------------------------------------------
unsigned int TDSFile::GetAudioLength()
{
	CheckOpened();
	CheckAudioStream();
	return AudioLength;
}

//---------------------------------------------------------------------------
BITMAPINFO* TDSFile::DIBGetFrameB(unsigned int Position)
{
	CheckRead();
	CheckVideoStream();

	if (Position != VideoPos)
	{
		if (Position == FrameInBuffer)
		{
			VideoPos = Position;
		}
		else
		{
			SeekVideo(Position);
		}
	}

	if (VideoPos >= Length)
	{
		throw AVFileErrorGetVideo();
	}

	if (FrameInBuffer == VideoPos)
	{
		VideoPos++;
		return Bitmap;
	}

	HRESULT hr;
	bool Run = Internal->VideoCB->Buffers.empty();
	if (Run)
	{
		hr = Internal->pControl->Run();
		if (hr != S_OK)
		{
			OAFilterState State;
			hr = Internal->pControl->GetState(INFINITE, &State);
			if (hr == E_FAIL)
			{
				throw AVFileErrorGetVideo("Could not run graph!");
			}
		}
	}

	OAEVENT hEvent;
	hr = Internal->pMediaEvent->GetEventHandle(&hEvent);
	if (hr != S_OK)
	{
		throw AVFileErrorGetVideo("Handle error!");
	}

	HANDLE Events[2];
	Events[0] = Internal->VideoCB->Buffers.Event;
	Events[1] = (HANDLE)hEvent;
	bool Complete = false;
	while (!Complete)
	{
		hr = WaitForMultipleObjects(sizeof(Events) / sizeof(Events[0]), Events, FALSE, INFINITE);
		if (hr == (HRESULT)WAIT_OBJECT_0 + 1)
		{
			long evCode;
			LONG_PTR param1, param2;
			HRESULT hr;
			while (SUCCEEDED(Internal->pMediaEvent->GetEvent(&evCode, &param1, &param2, 0)))
			{
				Internal->pMediaEvent->FreeEventParams(evCode, param1, param2);
				if (evCode == EC_COMPLETE)
				{
					Complete = true;
					break;
				}
			}
		}
		else if (hr == (HRESULT)WAIT_OBJECT_0)
		{
			break;
		}
		else
		{
			throw AVFileErrorGetVideo("Wait error!");
		}
	}

	if (Run)
	{
		Internal->pControl->Pause();
	}

	if (!Complete)
	{
		TBuffer *Buffer = Internal->VideoCB->Buffers.pop_front();
		if (Buffer->pBuffer == NULL)
		{
			delete Buffer;
			throw AVFileErrorMemory();
		}
		memcpy(BitmapData, Buffer->pBuffer, min(Buffer->BufferSize, long(Bitmap->bmiHeader.biSizeImage)));
		delete Buffer;
	}
	else
	{
		memset(BitmapData, 0, Bitmap->bmiHeader.biSizeImage);
	}

	FrameInBuffer = VideoPos;
	VideoPos++;
	return Bitmap;
}

//---------------------------------------------------------------------------
void TDSFile::SkipFrames(unsigned int Frames)
{
	CheckRead();
	CheckVideoStream();

	if (Frames == 0)
	{
		return;
	}

	if ((VideoPos + Frames) >= Length)
	{
		throw AVFileErrorGetVideo();
	}

	HRESULT hr;

	OAEVENT hEvent;
	hr = Internal->pMediaEvent->GetEventHandle(&hEvent);
	if (hr != S_OK)
	{
		throw AVFileErrorGetVideo("Handle error!");
	}

	HANDLE Events[2];
	Events[0] = Internal->VideoCB->Buffers.Event;
	Events[1] = (HANDLE)hEvent;
	bool Complete = false;

	bool Run = false;
	while (Frames > 0)
	{
		if (Internal->VideoCB->Buffers.empty() && !Run)
		{
			hr = Internal->pControl->Run();
			if (hr != S_OK)
			{
				OAFilterState State;
				hr = Internal->pControl->GetState(INFINITE, &State);
				if (hr == E_FAIL)
				{
					throw AVFileErrorGetVideo("Could not run graph!");
				}
			}
			Run = true;
		}

		while (!Complete)
		{
			hr = WaitForMultipleObjects(sizeof(Events) / sizeof(Events[0]), Events, FALSE, INFINITE);
			if (hr == (HRESULT)WAIT_OBJECT_0 + 1)
			{
				long evCode;
				LONG_PTR param1, param2;
				HRESULT hr;
				while (SUCCEEDED(Internal->pMediaEvent->GetEvent(&evCode, &param1, &param2, 0)))
				{
					Internal->pMediaEvent->FreeEventParams(evCode, param1, param2);
					if (evCode == EC_COMPLETE)
					{
						Complete = true;
						break;
					}
				}
			}
			else if (hr == (HRESULT)WAIT_OBJECT_0)
			{
				break;
			}
			else
			{
				throw AVFileErrorGetVideo("Wait error!");
			}
		}
		if (!Complete)
		{
			delete Internal->VideoCB->Buffers.pop_front();
		}
		Frames--;
		VideoPos++;
	}

	if (Run)
	{
		Internal->pControl->Pause();
	}
}

//---------------------------------------------------------------------------
BYTE* TDSFile::GetFrameB(unsigned int Position)
{
	BITMAPINFO *Bitmap = DIBGetFrameB(Position);
	BYTE *Src = (BYTE*)Bitmap + Bitmap->bmiHeader.biSize + Bitmap->bmiHeader.biSizeImage;
	BYTE *Dst = FrameBuffer;
	for (int i = 0; i < VideoInfo.Height; i++)
	{
		Src -= AlignedScanLineWidth;
		memcpy(Dst, Src, ScanLineWidth);
		Dst += ScanLineWidth;
	}
	return FrameBuffer;
}

//---------------------------------------------------------------------------
void TDSFile::GetFrameB2(BYTE *Buffer, unsigned int Position)
{
	BITMAPINFO *Bitmap = DIBGetFrameB(Position);
	BYTE *Src = (BYTE*)Bitmap + Bitmap->bmiHeader.biSize + Bitmap->bmiHeader.biSizeImage;
	BYTE *Dst = Buffer;
	for (int i = 0; i < VideoInfo.Height; i++)
	{
		Src -= AlignedScanLineWidth;
		memcpy(Dst, Src, ScanLineWidth);
		Dst += ScanLineWidth;
	}
}

//---------------------------------------------------------------------------
BYTE* TDSFile::GetAudio(unsigned int &Size)
{
	CheckRead();
	CheckAudioStream();

	if (AudioPos >= AudioLength)
	{
		throw AVFileErrorGetAudio();
	}

	HRESULT hr;
	bool Run = Internal->AudioCB->Buffers.empty();
	if (Run)
	{
		hr = Internal->pControl->Run();
		if (hr != S_OK)
		{
			OAFilterState State;
			hr = Internal->pControl->GetState(INFINITE, &State);
			if (hr == E_FAIL)
			{
				throw AVFileErrorGetAudio("Could not run graph!");
			}
		}
	}

	OAEVENT hEvent;
	hr = Internal->pMediaEvent->GetEventHandle(&hEvent);
	if (hr != S_OK)
	{
		throw AVFileErrorGetVideo("Handle error!");
	}

	HANDLE Events[2];
	Events[0] = Internal->AudioCB->Buffers.Event;
	Events[1] = (HANDLE)hEvent;
	bool Complete = false;
	while (!Complete)
	{
		hr = WaitForMultipleObjects(sizeof(Events) / sizeof(Events[0]), Events, FALSE, INFINITE);
		if (hr == (HRESULT)WAIT_OBJECT_0 + 1)
		{
			long evCode;
			LONG_PTR param1, param2;
			HRESULT hr;
			while (SUCCEEDED(Internal->pMediaEvent->GetEvent(&evCode, &param1, &param2, 0)))
			{
				Internal->pMediaEvent->FreeEventParams(evCode, param1, param2);
				if (evCode == EC_COMPLETE)
				{
					Complete = true;
					break;
				}
			}
		}
		else if (hr == (HRESULT)WAIT_OBJECT_0)
		{
			break;
		}
		else
		{
			throw AVFileErrorGetAudio("Wait error!");
		}
	}

	if (Run)
	{
		Internal->pControl->Pause();
	}

	if (!Complete)
	{
		TBuffer *Buffer = Internal->AudioCB->Buffers.pop_front();
		if (Buffer->pBuffer == NULL)
		{
			delete Buffer;
			throw AVFileErrorMemory();
		}
		if (AudioBuffer == NULL || (long)AudioBufferSize != Buffer->BufferSize)
		{
			if (AudioBuffer != NULL)
			{
				FreeMem(AudioBuffer);
			}
			AudioBuffer = (BYTE*)GetMem(Buffer->BufferSize);
			if (AudioBuffer == NULL)
			{
				delete Buffer;
				throw AVFileErrorMemory();
			}
			AudioBufferSize = Buffer->BufferSize;
		}
		memcpy(AudioBuffer, Buffer->pBuffer, Buffer->BufferSize);
		Size = Buffer->BufferSize;
		delete Buffer;
	}
	else
	{
		AudioBufferSize = Size = (AudioLength - AudioPos) * (2 * AudioInfo.Channels);
		if (AudioBuffer != NULL)
		{
			FreeMem(AudioBuffer);
		}
		AudioBuffer = (BYTE*)GetMem(AudioBufferSize);
		if (AudioBuffer == NULL)
		{
			throw AVFileErrorMemory();
		}
		memset(AudioBuffer, 0, AudioBufferSize);
	}
	AudioPos += Size / (2 * AudioInfo.Channels);
	return AudioBuffer;
}

//---------------------------------------------------------------------------
void TDSFile::SkipAudio()
{
	CheckRead();
	CheckAudioStream();

	if (AudioPos >= AudioLength)
	{
		throw AVFileErrorGetAudio();
	}

	Internal->AudioCB->Buffers.Enter();
	unsigned int Size = 0;
	while (!Internal->AudioCB->Buffers.empty())
	{
		TBuffer *Buffer = Internal->AudioCB->Buffers.pop_front();
		Size += Buffer->BufferSize;
		delete Buffer;
	}
	Internal->AudioCB->Buffers.Leave();
	AudioPos += Size / (2 * AudioInfo.Channels);
}

//---------------------------------------------------------------------------
void TDSFile::Seek(unsigned int Frame)
{
	CheckOpened();

	if (OpenMode == omRead)
	{
		HRESULT hr;
		OAFilterState State;
		REFERENCE_TIME Start = Frame;

		if (OpenType & otVideo)
		{
			hr = Internal->pVideoGrabber->SetCallback(NULL, 1);
			if (hr != S_OK)
			{
				throw AVFileErrorRead("Could not setup callback!");
			}
		}

		if (OpenType & otAudio)
		{
			hr = Internal->pAudioGrabber->SetCallback(NULL, 1);
			if (hr != S_OK)
			{
				throw AVFileErrorRead("Could not setup callback!");
			}
		}

		hr = Internal->pControl->Stop();
		if (hr != S_OK)
		{
			Internal->pControl->GetState(INFINITE, &State);
			if (hr == E_FAIL)
			{
				throw AVFileErrorRead("Could not stop graph!");
			}
		}

		hr = Internal->pSeeking->SetTimeFormat(&TIME_FORMAT_FRAME);
		if (hr != S_OK)
		{
			throw AVFileErrorRead("Could not set time format!");
		}

		hr = Internal->pSeeking->SetPositions(&Start, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
		if (hr != S_OK)
		{
			throw AVFileErrorRead("Could not set position!");
		}

		if (OpenType & otVideo)
		{
			hr = Internal->pVideoGrabber->SetCallback(Internal->VideoCB, 1);
			if (hr != S_OK)
			{
				throw AVFileErrorRead("Could not setup callback!");
			}
			Internal->VideoCB->Buffers.clear();
			VideoPos = Frame;
		}

		if (OpenType & otAudio)
		{
			hr = Internal->pAudioGrabber->SetCallback(Internal->AudioCB, 1);
			if (hr != S_OK)
			{
				throw AVFileErrorRead("Could not setup callback!");
			}
			Internal->AudioCB->Buffers.clear();
			AudioPos = double(AudioInfo.SamplesPerSec) * double(Frame) * double(VideoInfo.FrameRate.Scale) / double(VideoInfo.FrameRate.Rate);
		}
	}
}

//---------------------------------------------------------------------------
void TDSFile::SeekVideo(unsigned int Frame)
{
	CheckOpened();
	CheckVideoStream();
	Seek(Frame);
}

//---------------------------------------------------------------------------
void TDSFile::SeekAudio(unsigned int Sample)
{
	CheckOpened();
	CheckAudioStream();
	Seek(Round((((double(Sample) / double(AudioInfo.SamplesPerSec)) * double(VideoInfo.FrameRate.Rate)) / double(VideoInfo.FrameRate.Scale))));
}

