//---------------------------------------------------------------------------
//	AVFile - library for audio and video files processing
//  2002 - 2007 Stanislav Sumec <sumec@fit.vutbr.cz>
//
//	avfile.cpp
//  base class
//
//  TO-DO
//  -----
//    - merge TDSFile + TDVFile
//
//  Revision history (Only major modifications are noted.)
//  ------------------------------------------------------
//
//  21.2.2007
//    - TFFFile - fix Seek (not 100% working!)
//    - support for MinGW
//  16.4.2007
//    - TBMPFile - suport for 24 bit bitmaps
//  10.2.2007
//    - TDVFile - suport for RTSP via Elecard filter
//    - TFFFile - support for MSVC
//    - avfile_utils
//    - optional configuration from avfile_config.h, AVFILE_HAVE_CONFIG have to be defined
//    - new define AVFILE_WITH_DIGILIB, removed AVFILE_LIB_WITHOUT_DIGILIB and AVFILE_HAVE_SRC
//  11.1.2006
//    - TDVFile - method GetDevices and opening devices according to names
//  12.10.2005
//    - TFFFile - support for actual CVS version
//  23.4.2005
//    - TFFFile - fix SkipAudio, Seek, SeekAudio
//    - TFFFile - deinterlace (experimental, support only frames with dimensions divisible by 4)
//    - TDSFile - deadlock fix
//  22.4.2005
//    - TFFFile - fix Open
//    - TFFFile - fix for incorrect video stream length
//   8.4.2005
//    - TFFFile - fix Seek (not 100% working!)
//    - TFFFile - SkipFrames
//    - TAVIFile - fix PutFrame for incompatible image size with compressor
//  25.3.2005
//    - changed pixel fromat in GetFrame and PutFrame methods (BGR->BGRA)
//    - added methods GetFrame with Buffer parameter
//    - DLGetFrame optimalization (uses NewImageReferenceExternal)
//  25.2.2005
//    - DLGetFrame with return type ImageStruct, optimalization for ImageRGB
//    - DLPutFrame optimalization for ImageRGB
//    - internal modifications (private member Implemented)
//  19.2.2005
//    - GetFrame internal modifications
//    - new methods GetCapabilities, SkipFrames, SkipAudio
//    - TDSFile - rewriting
//   7.1.2005
//    - TFFFile - support for ffmpeg-0.4.9-pre1 (Warning, ffmpeg returns invalid length of streams.)
//    - TFFFile - adder parameter AudioBitRate
//  7.12.2004
//    - DLPutFrame can store Image8 and ImageFloat
// 27.10.2004
//    - TFFFile - fix SeekVideo
// 22.10.2004
//    - TFFFile - audio reading and writing
//    - DLGetFrame and DLPutFrame optimalization
// 15.7.2004
//    - TFFFile - video writing
//  9.4.2004
//    - TDSFile - added audio reading
// 20.3.2004
//    - generating DIB from GetFrame
//    - rewriten memory allocation
//    - Open has new bool parameter ThrowExceptions that is preset to true
// 13.3.2004
//    - TFFFile - only video reading
//  12.3.2004
//    - TBMPFile - works in Linux now
//  12.2.2004
//    - TDVFile - only video reading
//  10.2.2004
//    - TDSFile - only video reading
//  28.1.2004
//    - TBMPFile
//  24.1.2004
//    - throw vcl exceptions in C++ Builder
//  7.11.2003
//    - DIBGetFrame
//  16.4.2003
//    - exceptions with messages
//    - support for other PixelFormat in DLGetFrame
//  27.3.2003
//    - various modifications
//  1.10.2002
//    - first release (TAVFile, TAVIFile)
//---------------------------------------------------------------------------

#include <stdlib.h>
#include <math.h>
#ifdef __BORLANDC__
#include <ctype>
#endif
#include <locale>

#pragma hdrstop
#include "avfile.h"

//---------------------------------------------------------------------------
bool operator == (const TAVFile::TFrameRate& f1, const TAVFile::TFrameRate& f2)
{
	return f1.Rate == f2.Rate && f1.Scale == f2.Scale;
}

//---------------------------------------------------------------------------
bool operator < (const TAVFile::TFrameRate& f1, const TAVFile::TFrameRate& f2)
{
	return (double(f1.Rate) / double(f1.Scale)) < (double(f2.Rate) / double(f2.Scale));
}

//---------------------------------------------------------------------------
bool operator == (const TAVFile::TFrameInfo& f1, const TAVFile::TFrameInfo& f2)
{
	return f1.Width == f2.Width && f1.Height == f2.Height && f1.FrameRate == f2.FrameRate;
}

//---------------------------------------------------------------------------
#if defined(__BORLANDC__) && !defined(AVFILE_NO_VCL_EXCEPTIONS)
TAVFile::AVFileError::AVFileError(): Exception("") {};
TAVFile::AVFileError::AVFileError(const char *AMessage): Exception(AMessage) {};
#else
TAVFile::AVFileError::AVFileError() {};
TAVFile::AVFileError::AVFileError(const char *AMessage): Message(AMessage) {};
#endif
TAVFile::AVFileErrorMemory::AVFileErrorMemory(): AVFileError("Could not allocate memory!") {};
TAVFile::AVFileErrorMemory::AVFileErrorMemory(const char *AMessage): AVFileError(AMessage) {};
TAVFile::AVFileErrorNotImplemented::AVFileErrorNotImplemented(): AVFileError("Function is not implemented!") {};
TAVFile::AVFileErrorNotImplemented::AVFileErrorNotImplemented(const char *AMessage): AVFileError(AMessage) {};
TAVFile::AVFileErrorOpen::AVFileErrorOpen(): AVFileError("Could not open file!") {};
TAVFile::AVFileErrorOpen::AVFileErrorOpen(const char *AMessage): AVFileError(AMessage) {};
TAVFile::AVFileErrorOpenVideo::AVFileErrorOpenVideo(): AVFileErrorOpen("Could not open video stream!") {};
TAVFile::AVFileErrorOpenVideo::AVFileErrorOpenVideo(const char *AMessage): AVFileErrorOpen(AMessage) {};
TAVFile::AVFileErrorOpenAudio::AVFileErrorOpenAudio(): AVFileErrorOpen("Could not open audio stream!") {};
TAVFile::AVFileErrorOpenAudio::AVFileErrorOpenAudio(const char *AMessage): AVFileErrorOpen(AMessage) {};
TAVFile::AVFileErrorOpenInvalidMode::AVFileErrorOpenInvalidMode(): AVFileErrorOpen("Invalid open mode!") {};
TAVFile::AVFileErrorOpenInvalidMode::AVFileErrorOpenInvalidMode(const char *AMessage): AVFileErrorOpen(AMessage) {};
TAVFile::AVFileErrorNotOpened::AVFileErrorNotOpened(): AVFileError("File is not opened!") {};
TAVFile::AVFileErrorNotOpened::AVFileErrorNotOpened(const char *AMessage): AVFileError(AMessage) {};
TAVFile::AVFileErrorVideoNotOpened::AVFileErrorVideoNotOpened(): AVFileErrorNotOpened("Video stream is not opened!") {};
TAVFile::AVFileErrorVideoNotOpened::AVFileErrorVideoNotOpened(const char *AMessage): AVFileErrorNotOpened(AMessage) {};
TAVFile::AVFileErrorAudioNotOpened::AVFileErrorAudioNotOpened(): AVFileErrorNotOpened("Audio stream is not opened!") {};
TAVFile::AVFileErrorAudioNotOpened::AVFileErrorAudioNotOpened(const char *AMessage): AVFileErrorNotOpened(AMessage) {};
TAVFile::AVFileErrorRead::AVFileErrorRead(const char *AMessage): AVFileError(AMessage) {};
TAVFile::AVFileErrorGetVideo::AVFileErrorGetVideo(): AVFileErrorRead("Could not get frame!") {};
TAVFile::AVFileErrorGetVideo::AVFileErrorGetVideo(const char *AMessage): AVFileErrorRead(AMessage) {};
TAVFile::AVFileErrorGetAudio::AVFileErrorGetAudio(): AVFileErrorRead("Could not get audio!") {};
TAVFile::AVFileErrorGetAudio::AVFileErrorGetAudio(const char *AMessage): AVFileErrorRead(AMessage) {};
TAVFile::AVFileErrorWrite::AVFileErrorWrite(const char *AMessage): AVFileError(AMessage) {};
TAVFile::AVFileErrorPutVideo::AVFileErrorPutVideo(): AVFileErrorWrite("Could not put frame!") {};
TAVFile::AVFileErrorPutVideo::AVFileErrorPutVideo(const char *AMessage): AVFileErrorWrite(AMessage) {};
TAVFile::AVFileErrorPutAudio::AVFileErrorPutAudio(const char *AMessage): AVFileErrorWrite(AMessage) {};

//---------------------------------------------------------------------------
TAVFile::TAVFile()
{
	Implemented = 0;
	Capabilities = 0;
	DIBFrame = NULL;
	DIBFrameSize = 0;
#ifdef AVFILE_WITH_DIGILIB
	DLFrame = NULL;
#endif
	VideoInfo.Width = 320;
	VideoInfo.Height = 240;
	VideoInfo.FrameRate.Rate = 25;
	VideoInfo.FrameRate.Scale = 1;

	VideoInfo.FrameRate.Rate = 1000;
	VideoInfo.FrameRate.Scale = 1;

	AudioInfo.Channels = 2;
	AudioInfo.SamplesPerSec = 44100;
	OpenMode = omClosed;
	OpenType = otNone;

	Parameters.BitRate = 1500000;
	Parameters.KeyFrame = 10;
	Parameters.AudioBitRate = 128000;
	Parameters.Deinterlace = false;
	Parameters.Codec = "";
	Parameters.AudioCodec = "";

	Description = "";
}

//---------------------------------------------------------------------------
TAVFile::~TAVFile()
{
	if (DIBFrame != NULL)
	{
		FreeMem(DIBFrame);
	}
#ifdef AVFILE_WITH_DIGILIB
	if (DLFrame != NULL)
	{
		DeleteImage(DLFrame);
	}
#endif
}

//---------------------------------------------------------------------------
void* TAVFile::GetMem(size_t Size)
{
#if defined(_WIN32) || defined(WIN32)
	return (void*)GlobalAlloc(GMEM_FIXED, Size);
#else
	return malloc(Size);
#endif
}

//--------------------------------------------------------------------------
void TAVFile::FreeMem(void *Mem)
{
#if defined(_WIN32) || defined(WIN32)
	GlobalFree(HGLOBAL(Mem));
#else
	free(Mem);
#endif
}

//--------------------------------------------------------------------------
unsigned int TAVFile::GetCapabilities()
{
	return Capabilities;
};

//--------------------------------------------------------------------------
int TAVFile::GetOpenMode()
{
	return OpenMode;
};

//--------------------------------------------------------------------------
int TAVFile::GetOpenType()
{
	return OpenType;
};

//--------------------------------------------------------------------------
void TAVFile::OpenA(const char *FileName, int Mode, int Type, bool ThrowExceptions)
{
	if (ThrowExceptions)
	{
		throw AVFileErrorNotImplemented();
	}
}

//--------------------------------------------------------------------------
void TAVFile::Open(const char *FileName, int Mode, int Type, bool ThrowExceptions)
{
	OpenA(FileName, Mode, Type, ThrowExceptions);
}

//---------------------------------------------------------------------------
void TAVFile::CheckOpened()
{
	if (OpenMode == omClosed)
	{
		throw AVFileErrorNotOpened();
	}
}

//---------------------------------------------------------------------------
void TAVFile::CheckClosed()
{
	if (OpenMode != omClosed)
	{
		throw AVFileErrorOpenInvalidMode("File is already opened!");
	}
}

//---------------------------------------------------------------------------
void TAVFile::CheckRead()
{
	if (OpenMode != omRead)
	{
		throw AVFileErrorOpenInvalidMode("File is not opened for reading!");
	}
}

//---------------------------------------------------------------------------
void TAVFile::CheckWrite()
{
	if (OpenMode != omWrite)
	{
		throw AVFileErrorOpenInvalidMode("File is not opened for writing!");
	}
}

//---------------------------------------------------------------------------
void TAVFile::CheckVideoStream()
{
	if (!(OpenType & otVideo))
	{
		throw AVFileErrorVideoNotOpened();
	}
}

//---------------------------------------------------------------------------
void TAVFile::CheckAudioStream()
{
	if (!(OpenType & otAudio))
	{
		throw AVFileErrorAudioNotOpened();
	}
}

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

//---------------------------------------------------------------------------
void TAVFile::Seek(unsigned int Frame)
{
	CheckOpened();
	if (OpenType & otVideo)
	{
		SeekVideo(Frame);
	}
}

//---------------------------------------------------------------------------
BYTE* TAVFile::GetFrameA()
{
	if (Implemented & iGetFrameB)
	{
		return GetFrameB(GetVideoPos());
	}
	else
	{
		throw AVFileErrorNotImplemented();
	}
}

//---------------------------------------------------------------------------
BYTE* TAVFile::GetFrameB(unsigned int Position)
{
	if (Implemented & iGetFrameA)
	{
		SeekVideo(Position);
		return GetFrameA();
	}
	else
	{
		throw AVFileErrorNotImplemented();
	}
}

//---------------------------------------------------------------------------
void TAVFile::Frame2Frame(BYTE *Buffer1, BYTE *Buffer2)
{
	memcpy(Buffer2, Buffer1, VideoInfo.Width * VideoInfo.Height * 4);
}

//---------------------------------------------------------------------------
void TAVFile::GetFrameA2(BYTE *Buffer)
{
	if (Implemented & iGetFrameB2)
	{
		GetFrameB2(Buffer, GetVideoPos());
	}
	else
	{
		Frame2Frame(GetFrame(), Buffer);
	}
}

//---------------------------------------------------------------------------
void TAVFile::GetFrameB2(BYTE *Buffer, unsigned int Position)
{
	if (Implemented & iGetFrameA2)
	{
		SeekVideo(Position);
		GetFrameA2(Buffer);
	}
	else
	{
		return Frame2Frame(GetFrame(Position), Buffer);
	}
}

//---------------------------------------------------------------------------
BYTE* TAVFile::GetFrame()
{
	return GetFrameA();
}

//---------------------------------------------------------------------------
BYTE* TAVFile::GetFrame(unsigned int Position)
{
	return GetFrameB(Position);
}

//---------------------------------------------------------------------------
void TAVFile::GetFrame(BYTE *Buffer)
{
	GetFrameA2(Buffer);
}

//---------------------------------------------------------------------------
void TAVFile::GetFrame(BYTE *Buffer, unsigned int Position)
{
	GetFrameB2(Buffer, Position);
}

//---------------------------------------------------------------------------
void TAVFile::SkipFrames(unsigned int Frames)
{
	if (Frames > 0)
	{
		SeekVideo(GetVideoPos() + Frames);
	}
}

//---------------------------------------------------------------------------
void TAVFile::PutFrame(BYTE *Buffer)
{
	CheckWrite();
	CheckVideoStream();
	throw AVFileErrorNotImplemented();
}

//---------------------------------------------------------------------------
void TAVFile::DIBPutFrame(BITMAPINFO *Buffer)
{
	CheckWrite();
	CheckVideoStream();
	if (Buffer->bmiHeader.biCompression != BI_RGB ||
		Buffer->bmiHeader.biBitCount != 32 ||
		Buffer->bmiHeader.biWidth != VideoInfo.Width ||
		Buffer->bmiHeader.biHeight != VideoInfo.Height)
	{
		throw AVFileErrorPutVideo("Unsuported bitmap!");
	}

	BYTE *Frame = new BYTE[VideoInfo.Width * VideoInfo.Height * 4];
	try
	{
		int LineSize = Buffer->bmiHeader.biWidth * 4;
		BYTE *Src = ((BYTE*)Buffer) + Buffer->bmiHeader.biSize + Buffer->bmiHeader.biClrUsed * sizeof(RGBQUAD) + (Buffer->bmiHeader.biHeight - 1) * LineSize;
		BYTE *Dst = Frame;

		for (int y = 0; y < VideoInfo.Height; y++)
		{
			memcpy(Dst, Src, LineSize);
			Dst += LineSize;
			Src -= LineSize;
		}

		PutFrame(Frame);
		delete[] Frame;
	}
	catch (...)
	{
		delete[] Frame;
		throw;
	}
}

//---------------------------------------------------------------------------
unsigned int TAVFile::GetVideoLength()
{
	CheckOpened();
	CheckVideoStream();
	return 0;
}

//---------------------------------------------------------------------------
unsigned int TAVFile::GetVideoPos()
{
	CheckOpened();
	CheckVideoStream();
	return VideoPos;
}

//---------------------------------------------------------------------------
TAVFile::TFrameInfo TAVFile::GetFrameInfo()
{
	return VideoInfo;
}

//---------------------------------------------------------------------------
void TAVFile::SetFrameInfo(TFrameInfo Info)
{
	CheckClosed();
	VideoInfo = Info;
}

//---------------------------------------------------------------------------
void TAVFile::SeekVideo(unsigned int Frame)
{
	CheckOpened();
	CheckVideoStream();
	VideoPos = Frame;
};

//---------------------------------------------------------------------------
BYTE* TAVFile::GetAudio(unsigned int &Size)
{
	CheckRead();
	CheckAudioStream();
	throw AVFileErrorNotImplemented();
}

//---------------------------------------------------------------------------
void TAVFile::SkipAudio()
{
	CheckRead();
	CheckAudioStream();
}

//---------------------------------------------------------------------------
void TAVFile::PutAudio(BYTE *Buffer, unsigned int Size)
{
	CheckWrite();
	CheckAudioStream();
	throw AVFileErrorNotImplemented();
}

//---------------------------------------------------------------------------
unsigned int TAVFile::GetAudioLength()
{
	CheckOpened();
	CheckAudioStream();
	return 0;
}

//---------------------------------------------------------------------------
unsigned int TAVFile::GetAudioPos()
{
	CheckOpened();
	CheckAudioStream();
	return AudioPos;
}

//---------------------------------------------------------------------------
TAVFile::TAudioInfo TAVFile::GetAudioInfo()
{
	return AudioInfo;
}

//---------------------------------------------------------------------------
void TAVFile::SetAudioInfo(TAudioInfo Info)
{
	CheckClosed();
	AudioInfo = Info;
}

//---------------------------------------------------------------------------
void TAVFile::SeekAudio(unsigned int Sample)
{
	CheckOpened();
	CheckAudioStream();
	AudioPos = Sample;
}

//---------------------------------------------------------------------------
BITMAPINFO* TAVFile::Frame2DIB(BYTE *Buffer)
{
	unsigned int ScanLineWidth = VideoInfo.Width * 4;
	unsigned int AlignedScanLineWidth = DIB_SCAN_LINE(ScanLineWidth);
	unsigned int Size = sizeof(BITMAPINFOHEADER) + AlignedScanLineWidth * VideoInfo.Height;

	if (DIBFrame == NULL || DIBFrameSize != Size)
	{
		if (DIBFrame != NULL)
		{
			FreeMem(DIBFrame);
			DIBFrame = NULL;
			DIBFrameSize = 0;
		}
		DIBFrame = (BITMAPINFO*)GetMem(Size);
		if (DIBFrame == NULL)
		{
			throw AVFileErrorMemory();
		}
		DIBFrameSize = Size;
	}

	DIBFrame->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	DIBFrame->bmiHeader.biWidth = VideoInfo.Width;
	DIBFrame->bmiHeader.biHeight = VideoInfo.Height;
	DIBFrame->bmiHeader.biPlanes = 1;
	DIBFrame->bmiHeader.biBitCount = 32;
	DIBFrame->bmiHeader.biCompression = BI_RGB;
	DIBFrame->bmiHeader.biSizeImage = Size - sizeof(BITMAPINFOHEADER);
	DIBFrame->bmiHeader.biXPelsPerMeter = 0;
	DIBFrame->bmiHeader.biYPelsPerMeter = 0;
	DIBFrame->bmiHeader.biClrUsed = 0;
	DIBFrame->bmiHeader.biClrImportant = 0;

	BYTE *Src = Buffer;
	BYTE *Dst = (BYTE*)DIBFrame + DIBFrame->bmiHeader.biSize + DIBFrame->bmiHeader.biSizeImage;
	for (int i = 0; i < DIBFrame->bmiHeader.biHeight; i++)
	{
		Dst -= AlignedScanLineWidth;
		if (AlignedScanLineWidth > ScanLineWidth)
		{
			memcpy(Dst, Src, ScanLineWidth);
			memset(Dst + ScanLineWidth, 0, AlignedScanLineWidth - ScanLineWidth);
		}
		else
		{
			memcpy(Dst, Src, AlignedScanLineWidth);
		}
		Src += ScanLineWidth;
	}
	return DIBFrame;
}

//---------------------------------------------------------------------------
BITMAPINFO* TAVFile::DIBGetFrameA()
{
	if (Implemented & iDIBGetFrameB)
	{
		return DIBGetFrameB(GetVideoPos());
	}
	else
	{
		return Frame2DIB(GetFrame());
	}
}

//---------------------------------------------------------------------------
BITMAPINFO* TAVFile::DIBGetFrameB(unsigned int Position)
{
	if (Implemented & iDIBGetFrameA)
	{
		SeekVideo(Position);
		return DIBGetFrameA();
	}
	else
	{
		return Frame2DIB(GetFrame(Position));
	}
}

//---------------------------------------------------------------------------
BITMAPINFO* TAVFile::DIBGetFrame()
{
	return DIBGetFrameA();
}

//---------------------------------------------------------------------------
BITMAPINFO* TAVFile::DIBGetFrame(unsigned int Position)
{
	return DIBGetFrameB(Position);
}

#ifdef AVFILE_WITH_DIGILIB
//---------------------------------------------------------------------------
ImageStruct* TAVFile::Frame2Image(BYTE *Buffer)
{
	if (DLFrame != NULL)
	{
		DeleteImage(DLFrame);
		DLFrame = NULL;
	}
	DLFrame = NewImageReferenceExternal(VideoInfo.Width, VideoInfo.Height, 4, VideoInfo.Width * 4, ImageRGBLinear, Buffer, NULL, 0, NULL, NULL);
	if (DLFrame == NULL)
	{
		throw AVFileErrorMemory();
	}
	return DLFrame;
}

//---------------------------------------------------------------------------
void TAVFile::Frame2Image(BYTE *Buffer, ImageStruct *Image)
{
	if ((Image->PixelType & ~ImageLinearMask) != ImageRGB &&
		(Image->PixelType & ~ImageLinearMask) != Image8   &&
		(Image->PixelType & ~ImageLinearMask) != ImageFloat)
	{
		throw AVFileErrorGetVideo("Unsuported PixelType!");
	}

	int Width, Height;
	if (VideoInfo.Width < ImageXSize(Image))
	{
		Width = VideoInfo.Width;
	}
	else
	{
		Width = ImageXSize(Image);
	}
	int Delta =  4 * (VideoInfo.Width - Width);
	if (VideoInfo.Height < ImageYSize(Image))
	{
		Height = VideoInfo.Height;
	}
	else
	{
		Height = ImageYSize(Image);
	}

	unsigned char *Src = Buffer;
	if ((Image->PixelType & ~ImageLinearMask) == ImageRGB)
	{
		BYTE *Dst = Image->Raster;
		if (Image->XOffset == 4)
		{
			int SrcLineLen = 4 * VideoInfo.Width;
			int DstLineLen = 4 * Width;
			for (int j = 0; j < Height; j++)
			{
				memcpy(Dst, Src, DstLineLen);
				Src += SrcLineLen;
				Dst += Image->YOffset;
			}
		}
		else
		{
			for (int j = 0; j < Height; j++)
			{
				BYTE *DstLine = Dst;
				for (int i = 0; i < Width; i++)
				{
					*((int*)DstLine) = *((int*)Src);
					Src += 4;
					DstLine += Image->XOffset;
				}
				Src += Delta;
				Dst += Image->YOffset;
			}
		}
	}
	else if ((Image->PixelType & ~ImageLinearMask) == Image8)
	{
		for (int j = 0; j < Height; j++)
		{
			for (int i = 0; i < Width; i++)
			{
				ImagePixel(Image, i, j) = BYTE(0.114*Src[0] + 0.587*Src[1] + 0.299*Src[2]);
				Src += 4;
			}
			Src += Delta;
		}
	}
	else if ((Image->PixelType & ~ImageLinearMask) == ImageFloat)
	{
		for (int j = 0; j < Height; j++)
		{
			for (int i = 0; i < Width; i++)
			{
				float Pixel = (0.114*Src[0] + 0.587*Src[1] + 0.299*Src[2]) / 127.5 - 1.0;
				SetImageFloatPixel(Image, i, j, Pixel);
				Src += 4;
			}
			Src += Delta;
		}
	}
}

#if defined(_WIN32) || defined(WIN32)
//---------------------------------------------------------------------------
ImageStruct* TAVFile::DIB2Image(BITMAPINFO *Bitmap)
{
	if (DLFrame != NULL)
	{
		DeleteImage(DLFrame);
		DLFrame = NULL;
	}
	if (Bitmap->bmiHeader.biCompression == BI_RGB && Bitmap->bmiHeader.biBitCount == 32)
	{
		DLFrame = NewImageReferenceExternal(Bitmap->bmiHeader.biWidth, Bitmap->bmiHeader.biHeight, 4, -Bitmap->bmiHeader.biWidth * 4, ImageRGBLinear, (BYTE*)Bitmap + Bitmap->bmiHeader.biSize + (Bitmap->bmiHeader.biHeight - 1) * Bitmap->bmiHeader.biWidth * 4, NULL, 0, NULL, NULL);
	}
	else
	{
		unsigned int ScanLineWidth = Bitmap->bmiHeader.biWidth * 4;
		unsigned int AlignedScanLineWidth = DIB_SCAN_LINE(ScanLineWidth);
		unsigned int Size = sizeof(BITMAPINFOHEADER) + AlignedScanLineWidth * Bitmap->bmiHeader.biHeight;

		if (DIBFrame == NULL || DIBFrameSize != Size)
		{
			if (DIBFrame != NULL)
			{
				FreeMem(DIBFrame);
				DIBFrame = NULL;
				DIBFrameSize = 0;
			}
			DIBFrame = (BITMAPINFO*)GetMem(Size);
			if (DIBFrame == NULL)
			{
				throw AVFileErrorMemory();
			}
			DIBFrameSize = Size;
		}

		DIBFrame->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		DIBFrame->bmiHeader.biWidth = Bitmap->bmiHeader.biWidth;
		DIBFrame->bmiHeader.biHeight = Bitmap->bmiHeader.biHeight;
		DIBFrame->bmiHeader.biPlanes = 1;
		DIBFrame->bmiHeader.biBitCount = 32;
		DIBFrame->bmiHeader.biCompression = BI_RGB;
		DIBFrame->bmiHeader.biSizeImage = Size - sizeof(BITMAPINFOHEADER);
		DIBFrame->bmiHeader.biXPelsPerMeter = 0;
		DIBFrame->bmiHeader.biYPelsPerMeter = 0;
		DIBFrame->bmiHeader.biClrUsed = 0;
		DIBFrame->bmiHeader.biClrImportant = 0;

		DWORD dwSourceBitsSize = Bitmap->bmiHeader.biHeight * DIB_SCAN_LINE((Bitmap->bmiHeader.biWidth * Bitmap->bmiHeader.biPlanes * Bitmap->bmiHeader.biBitCount) / 8);

		LPBITMAPINFO lpbmi = DIBFrame;
		LPBYTE lpSourceBits, lpTargetBits, lpResult;
		HDC hDC = NULL, hSourceDC, hTargetDC;
		HBITMAP hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap;
		DWORD dwTargetBitsSize, dwTargetHeaderSize;

		hDC = GetDC(NULL);
		hTargetBitmap = CreateDIBSection(hDC, lpbmi, DIB_RGB_COLORS, &(void*)lpTargetBits, NULL, 0);
		hSourceBitmap = CreateDIBSection(hDC, Bitmap, DIB_RGB_COLORS, &(void*)lpSourceBits, NULL, 0);
		hSourceDC = CreateCompatibleDC(hDC);
		hTargetDC = CreateCompatibleDC(hDC);

		memcpy(lpSourceBits, (BYTE*)Bitmap + Bitmap->bmiHeader.biSize + Bitmap->bmiHeader.biClrUsed * sizeof(RGBQUAD), dwSourceBitsSize);
		hOldSourceBitmap = SelectObject(hSourceDC, hSourceBitmap);
		hOldTargetBitmap = SelectObject(hTargetDC, hTargetBitmap);

		BitBlt(hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY);

		SelectObject(hSourceDC, hOldSourceBitmap);
		SelectObject(hSourceDC, hOldTargetBitmap);
		DeleteDC(hSourceDC);
		DeleteDC(hTargetDC);
		ReleaseDC(NULL, hDC);
		GdiFlush();

		memcpy((BYTE*)DIBFrame + DIBFrame->bmiHeader.biSize, lpTargetBits, DIBFrame->bmiHeader.biSizeImage);
		DLFrame = NewImageReferenceExternal(DIBFrame->bmiHeader.biWidth, DIBFrame->bmiHeader.biHeight, 4, -DIBFrame->bmiHeader.biWidth * 4, ImageRGBLinear, (BYTE*)DIBFrame + DIBFrame->bmiHeader.biSize + (DIBFrame->bmiHeader.biHeight - 1) * DIBFrame->bmiHeader.biWidth * 4, NULL, 0, NULL, NULL);

		DeleteObject(hTargetBitmap);
		DeleteObject(hSourceBitmap);
	}
	if (DLFrame == NULL)
	{
		throw AVFileErrorMemory();
	}
	return DLFrame;
}
#endif

//---------------------------------------------------------------------------
ImageStruct* TAVFile::DLGetFrameA()
{
	if (Implemented & iDLGetFrameB)
	{
		return DLGetFrameB(GetVideoPos());
	}
#if defined(_WIN32) || defined(WIN32)
	else if (Implemented & (iDIBGetFrameA | iDIBGetFrameB))
	{
		return DIB2Image(DIBGetFrame());
	}
#endif
	else
	{
		return Frame2Image(GetFrame());
	}
}

//---------------------------------------------------------------------------
ImageStruct* TAVFile::DLGetFrameB(unsigned int Position)
{
	if (Implemented & iDLGetFrameA)
	{
		SeekVideo(Position);
		return DLGetFrameA();
	}
#if defined(_WIN32) || defined(WIN32)
	else if (Implemented & (iDIBGetFrameA | iDIBGetFrameB))
	{
		return DIB2Image(DIBGetFrame(Position));
	}
#endif
	else
	{
		return Frame2Image(GetFrame(Position));
	}
}

//---------------------------------------------------------------------------
void TAVFile::DLGetFrameA2(ImageStruct *Buffer)
{
	if (Implemented & iDLGetFrameB2)
	{
		DLGetFrameB2(Buffer, GetVideoPos());
	}
	else
	{
		Frame2Image(GetFrame(), Buffer);
	}
}

//---------------------------------------------------------------------------
void TAVFile::DLGetFrameB2(ImageStruct *Buffer, unsigned int Position)
{
	if (Implemented & iDLGetFrameA2)
	{
		SeekVideo(Position);
		DLGetFrameA2(Buffer);
	}
	else
	{
		Frame2Image(GetFrame(Position), Buffer);
	}
}

//---------------------------------------------------------------------------
ImageStruct* TAVFile::DLGetFrame()
{
	return DLGetFrameA();
}

//---------------------------------------------------------------------------
ImageStruct* TAVFile::DLGetFrame(unsigned int Position)
{
	return DLGetFrameB(Position);
}

//---------------------------------------------------------------------------
void TAVFile::DLGetFrame(ImageStruct *Buffer)
{
	DLGetFrameA2(Buffer);
}

//---------------------------------------------------------------------------
void TAVFile::DLGetFrame(ImageStruct *Buffer, unsigned int Position)
{
	DLGetFrameB2(Buffer, Position);
}

//---------------------------------------------------------------------------
void TAVFile::DLPutFrame(ImageStruct *Buffer)
{
	if ((Buffer->PixelType&~ImageLinearMask) != ImageRGB &&
		(Buffer->PixelType&~ImageLinearMask) != Image8   &&
		(Buffer->PixelType&~ImageLinearMask) != ImageFloat)
	{
		throw AVFileErrorPutVideo("Unsuported PixelType!");
	}

	unsigned int Size = 4 * VideoInfo.Width * VideoInfo.Height;
	BYTE *Out = (BYTE*)GetMem(Size);
	if (Out == NULL)
	{
		throw AVFileErrorMemory();
	}

	int Width, Height;
	if (VideoInfo.Width < ImageXSize(Buffer))
	{
		Width = VideoInfo.Width;
	}
	else
	{
		Width = ImageXSize(Buffer);
	}
	int Delta =  4 * (VideoInfo.Width - Width);
	if (VideoInfo.Height < ImageYSize(Buffer))
	{
		Height = VideoInfo.Height;
	}
	else
	{
		Height = ImageYSize(Buffer);
	}

	if (Width < VideoInfo.Width || Height < VideoInfo.Height)
	{
		memset(Out, 0, Size);
	}

	unsigned char *Dst = Out;
	if ((Buffer->PixelType & ~ImageLinearMask) == ImageRGB)
	{
		BYTE *Src = Buffer->Raster;
		if (Buffer->XOffset == 40)
		{
			int SrcLineLen = 4 * Width;
			int DstLineLen = 4 * VideoInfo.Width;
			for (int j = 0; j < Height; j++)
			{
				memcpy(Dst, Src, SrcLineLen);
				Dst += DstLineLen;
				Src += Buffer->YOffset;
			}
		}
		else
		{
			for (int j = 0; j < Height; j++)
			{
				BYTE *SrcLine = Src;
				for (int i = 0; i < Width; i++)
				{
					*((int*)Dst) = *((int*)SrcLine);
					SrcLine += Buffer->XOffset;
					Dst += 4;
				}
				Src += Buffer->YOffset;
				Dst += Delta;
			}
		}
	}
	else if ((Buffer->PixelType & ~ImageLinearMask) == Image8)
	{
		for (int j = 0; j < Height; j++)
		{
			for (int i = 0; i < Width; i++)
			{
				unsigned char Pixel = ImagePixel(Buffer, i, j);
				*Dst++ = Pixel;
				*Dst++ = Pixel;
				*Dst++ = Pixel;
				*Dst++ = 0;
			}
			Dst += Delta;
		}
	}
	else if ((Buffer->PixelType & ~ImageLinearMask) == ImageFloat)
	{
		for (int j = 0; j < Height; j++)
		{
			for (int i = 0; i < Width; i++)
			{
				unsigned char Pixel = BYTE((ImageFloatPixel(Buffer, i, j) + 1.0) * 127.5);
				*Dst++ = Pixel;
				*Dst++ = Pixel;
				*Dst++ = Pixel;
				*Dst++ = 0;
			}
			Dst += Delta;
		}
	}
	PutFrame(Out);
	FreeMem(Out);
}
#endif

//--------------------------------------------------------------------------
int TAVFile::Round(double v)
{
	if (v < 0)
	{
		return (int(v) - v < 0.5) ? int(v) : int(v) - 1;
	}
	else
	{
		return (v - int(v) < 0.5) ? int(v) : int(v) + 1;
	}
}

//--------------------------------------------------------------------------
void TAVFile::RegisterFileType(const char *Ext)
{
	FileTypes.push_back(Ext);
}

//--------------------------------------------------------------------------
bool TAVFile::FileTypeSupported(const char *Ext)
{
	std::string t;
#ifndef _MSC_VER
	for (int i = 0; Ext[i] != 0; i++)
	t += std::tolower(Ext[i]);
#endif
	for (std::vector<std::string>::iterator i = FileTypes.begin(); i != FileTypes.end(); i++)
	{
		if (*i == t)
		{
			return true;
		}
	}
	return false;
}

//---------------------------------------------------------------------------
TAVFile::TParameters TAVFile::GetParameters()
{
	return Parameters;
}

//---------------------------------------------------------------------------
void TAVFile::SetParameters(TParameters Parameters)
{
	this->Parameters = Parameters;
}

//---------------------------------------------------------------------------
TAVFile::TDescription TAVFile::GetDescription()
{
	return Description;
}

//---------------------------------------------------------------------------
int TAVFile::GetAudioSkew()
{
	return 0;
}





