Blob Blame Raw


#if _MSC_VER >= 1400
#define _CRT_SECURE_NO_DEPRECATE 1
#endif

#define _WIN32_DCOM

#define _WIN32_WINNT 0x0500
#include "tsystem.h"

#include <windows.h>
#include <objbase.h>
#include "texception.h"
#include "tiio_avi.h"
#include "tsound.h"
#include "tconvert.h"

#include "timageinfo.h"
#include "trasterimage.h"

//------------------------------------------------------------------------------

namespace
{

#define DibPtr(lpbi) (LPBYTE)(DibColors(lpbi) + (UINT)(lpbi)->biClrUsed)
#define DibColors(lpbi) ((LPRGBQUAD)((LPBYTE)(lpbi) + (int)(lpbi)->biSize))

//-----------------------------------------------------------

void WideChar2Char(LPCWSTR wideCharStr, char *str, int strBuffSize)
{
	WideCharToMultiByte(CP_ACP, 0, wideCharStr, -1, str, strBuffSize, 0, 0);
}

//------------------------------------------------------------------------------

std::string buildAVIExceptionString(int rc)
{
	switch (rc) {
		CASE AVIERR_BADFORMAT : return "The file couldn't be read, indicating a corrupt file or an unrecognized format.";
		CASE AVIERR_MEMORY : return "The file could not be opened because of insufficient memory.";
		CASE AVIERR_FILEREAD : return "A disk error occurred while reading the file.";
		CASE AVIERR_FILEOPEN : return "A disk error occurred while opening the file.";
		CASE REGDB_E_CLASSNOTREG : return "According to the registry, the type of file specified in m_aviFileOpen does not have a handler to process it.";

		CASE AVIERR_UNSUPPORTED : return "Format unsupported";
		CASE AVIERR_INTERNAL : return "Internal error";
		CASE AVIERR_NODATA : return "No data";

	DEFAULT:
		return "Unable to create avi.";
	}
}

//------------------------------------------------------------------------------

std::string SplitFourCC(DWORD fcc)
{
	std::string s;
	s += (char((fcc & 0x000000ff) >> 0));
	s += (char((fcc & 0x0000ff00) >> 8));
	s += (char((fcc & 0x00ff0000) >> 16));
	s += (char((fcc & 0xff000000) >> 24));

	return s;
}

//-----------------------------------------------------------

TRaster32P DIBToRaster(UCHAR *pDIBImage, int width, int height)
{
	assert(pDIBImage);
	if (!pDIBImage)
		return TRaster32P();

	//BITMAPINFOHEADER *bihdr = reinterpret_cast<BITMAPINFOHEADER *>(pDIBImage);
	UCHAR *rawData = pDIBImage;

	TRaster32P rasOut32(width, height);
	//ULONG imgSize = bihdr->biSizeImage;

	int ly = rasOut32->getLy();
	int n = 0;
	rasOut32->lock();
	while (n < ly) {
		TPixel32 *pix = rasOut32->pixels(n);
		TPixel32 *endPix = pix + rasOut32->getLx();
		while (pix < endPix) {
			pix->b = *rawData;
			rawData++;
			pix->g = *rawData;
			rawData++;
			pix->r = *rawData;
			rawData++;
			pix->m = 255;
			++pix;
			//rawData += 3;
		}
		++n;
	}
	rasOut32->unlock();
	return rasOut32;
}

//-----------------------------------------------------------

int getPrevKeyFrame(PAVISTREAM videoStream, int index)
{
	return AVIStreamFindSample(videoStream, index, FIND_PREV | FIND_KEY);
}

//-----------------------------------------------------------

bool isAKeyFrame(PAVISTREAM videoStream, int index)
{
	return index == getPrevKeyFrame(videoStream, index);
}

}; //end of namespace

//===========================================================
//
//  TImageWriterAvi
//
//===========================================================

class TImageWriterAvi : public TImageWriter
{

public:
	int m_frameIndex;

	TImageWriterAvi(const TFilePath &path, int frameIndex, TLevelWriterAvi *lwa)
		: TImageWriter(path), m_frameIndex(frameIndex), m_lwa(lwa)
	{
		m_lwa->addRef();
	}
	~TImageWriterAvi() { m_lwa->release(); }

	bool is64bitOutputSupported() { return false; }
	void save(const TImageP &img) { m_lwa->save(img, m_frameIndex); }

private:
	TLevelWriterAvi *m_lwa;

	//not implemented
	TImageWriterAvi(const TImageWriterAvi &);
	TImageWriterAvi &operator=(const TImageWriterAvi &src);
};

//===========================================================
//
//  TLevelWriterAvi;
//
//===========================================================

#ifdef _WIN32

TLevelWriterAvi::TLevelWriterAvi(const TFilePath &path, TPropertyGroup *winfo)
	: TLevelWriter(path, winfo), m_aviFile(0), m_videoStream(0), m_audioStream(0), m_bitmapinfo(0), m_outputFmt(0), m_hic(0), m_initDone(false), IOError(0), m_st(0), m_bpp(32), m_maxDataSize(0), m_buffer(0), m_firstframe(-1)
{
	CoInitializeEx(0, COINIT_MULTITHREADED);

	m_frameRate = 12.;

	AVIFileInit();

	if (TSystem::doesExistFileOrLevel(m_path))
		TSystem::deleteFile(m_path);

	int rc = AVIFileOpenW(&m_aviFile, m_path.getWideString().c_str(), OF_CREATE | OF_WRITE, 0);
	if (rc != 0)
		throw TImageException(getFilePath(), "Unable to create file");
	m_delayedFrames.clear();
}

//-----------------------------------------------------------

TLevelWriterAvi::~TLevelWriterAvi()
{
	//controllo che non ci siano ancora dei frame in coda nel codec (alcuni codec sono asincroni!)
	while (!m_delayedFrames.empty()) {
		LONG lSampWritten, lBytesWritten;
		int frameIndex = m_delayedFrames.front();
		DWORD flagsOut = 0;
		DWORD flagsIn = !frameIndex ? AVIIF_KEYFRAME : 0;
		BITMAPINFOHEADER outHeader;
		void *bufferOut = 0;
		int res = compressFrame(&outHeader, &bufferOut, frameIndex, flagsIn, flagsOut);
		if (res != ICERR_OK) {
			if (bufferOut)
				_aligned_free(bufferOut);
			break;
		}
		if (AVIStreamWrite(m_videoStream, frameIndex, 1, bufferOut,
						   outHeader.biSizeImage,
						   flagsOut,
						   &lSampWritten, &lBytesWritten)) {
			if (bufferOut)
				_aligned_free(bufferOut);
			break;
		}
		m_delayedFrames.pop_front();
		if (bufferOut)
			_aligned_free(bufferOut);
	}

	if (m_st) {
		doSaveSoundTrack();
		m_st = 0;
	}

	if (m_hic) {
		ICCompressEnd(m_hic);
		ICClose(m_hic);
	}

	if (m_bitmapinfo)
		free(m_bitmapinfo);
	if (m_outputFmt)
		free(m_outputFmt);

	if (m_videoStream)
		AVIStreamClose(m_videoStream);

	if (m_audioStream)
		AVIStreamClose(m_audioStream);

	if (m_aviFile)
		AVIFileClose(m_aviFile);

	AVIFileExit();
	CoUninitialize();
	if (!m_delayedFrames.empty())
		throw TImageException(getFilePath(), "error compressing frame " + toString(m_delayedFrames.front()));
}

//-----------------------------------------------------------

void TLevelWriterAvi::searchForCodec()
{
	if (!m_properties)
		m_properties = new Tiio::AviWriterProperties();

	TEnumProperty *p = (TEnumProperty *)m_properties->getProperty("Codec");
	assert(p);
	std::wstring codecName = p->getValue();

	//--------  // cerco compressorName fra i codec

	HIC hic = 0;
	ICINFO icinfo;
	memset(&icinfo, 0, sizeof(ICINFO));
	bool found = false;
	if (codecName != L"Uncompressed") {
		char descr[2048], name[2048];
		DWORD fccType = 0;

		BITMAPINFO inFmt;
		memset(&inFmt, 0, sizeof(BITMAPINFO));

		inFmt.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		inFmt.bmiHeader.biWidth = inFmt.bmiHeader.biHeight = 100;
		inFmt.bmiHeader.biPlanes = 1;
		inFmt.bmiHeader.biCompression = BI_RGB;
		found = false;

		for (int bpp = 32; (bpp >= 24) && !found; bpp -= 8) {
			inFmt.bmiHeader.biBitCount = bpp;
			for (int i = 0; ICInfo(fccType, i, &icinfo); i++) {
				hic = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_COMPRESS);

				ICGetInfo(hic, &icinfo, sizeof(ICINFO)); // Find out the compressor name
				WideChar2Char(icinfo.szDescription, descr, sizeof(descr));
				WideChar2Char(icinfo.szName, name, sizeof(name));

				std::string compressorName;
				compressorName = std::string(name) + " '" + toString(bpp) + "' " + std::string(descr);

				if (hic) {
					if (ICCompressQuery(hic, &inFmt, NULL) != ICERR_OK) {
						ICClose(hic);
						continue; // Skip this compressor if it can't handle the format.
					}
					if (toWideString(compressorName) == codecName) {
						found = true;
						m_bpp = bpp;
						break;
					}
					ICClose(hic);
				}
			}
		}
	}
	m_hic = hic;
}

//-----------------------------------------------------------

void TLevelWriterAvi::createBitmap(int lx, int ly)
{
	const RGBQUAD NOMASK = {0x00, 0x00, 0x00, 0x00};
	m_bitmapinfo = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFOHEADER) + 255 * sizeof(RGBQUAD));

	m_bitmapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_bitmapinfo->bmiHeader.biWidth = lx;
	m_bitmapinfo->bmiHeader.biHeight = ly;
	m_bitmapinfo->bmiHeader.biPlanes = 1;
	m_bitmapinfo->bmiHeader.biBitCount = m_bpp;
	m_bitmapinfo->bmiHeader.biCompression = BI_RGB;
	m_bitmapinfo->bmiHeader.biSizeImage = lx * ly * m_bpp / 8;
	m_bitmapinfo->bmiHeader.biXPelsPerMeter = 0;
	m_bitmapinfo->bmiHeader.biYPelsPerMeter = 0;
	m_bitmapinfo->bmiHeader.biClrUsed = 0;
	m_bitmapinfo->bmiHeader.biClrImportant = 0;

	m_bitmapinfo->bmiColors[0] = NOMASK;
	m_bitmapinfo->bmiColors[1] = NOMASK;
	m_bitmapinfo->bmiColors[2] = NOMASK;

	m_buffer = _aligned_malloc(m_bitmapinfo->bmiHeader.biSizeImage, 128);
}

//-----------------------------------------------------------

TImageWriterP TLevelWriterAvi::getFrameWriter(TFrameId fid)
{
	if (IOError != 0)
		throw TImageException(m_path, buildAVIExceptionString(IOError));
	if (fid.getLetter() != 0)
		return TImageWriterP(0);
	int index = fid.getNumber() - 1;
	TImageWriterAvi *iwa = new TImageWriterAvi(m_path, index, this);
	return TImageWriterP(iwa);
}

//-----------------------------------------------------------

void TLevelWriterAvi::save(const TImageP &img, int frameIndex)
{
	CoInitializeEx(0, COINIT_MULTITHREADED);

	if (m_firstframe < 0)
		m_firstframe = frameIndex;
	int index = frameIndex - m_firstframe;
	TRasterImageP image(img);
	int lx = image->getRaster()->getLx();
	int ly = image->getRaster()->getLy();

	int pixelSize = image->getRaster()->getPixelSize();

	if (pixelSize != 4)
		throw TImageException(getFilePath(), "Unsupported pixel type");

	QMutexLocker sl(&m_mutex);

	if (!m_initDone) {
		searchForCodec();
		createBitmap(lx, ly);

		DWORD num = DWORD(tround(m_frameRate * 100.0));
		DWORD den = 100;

		AVISTREAMINFO psi;
		memset(&psi, 0, sizeof(AVISTREAMINFO));
		psi.fccType = streamtypeVIDEO;
		if (m_hic) {
			ICINFO icinfo;
			ICGetInfo(m_hic, &icinfo, sizeof(ICINFO));
			psi.fccHandler = icinfo.fccHandler;
		}
		psi.dwScale = den;
		psi.dwRate = num;
		psi.dwQuality = ICQUALITY_DEFAULT;
		psi.rcFrame.right = lx;
		psi.rcFrame.bottom = ly;
		::strcpy(psi.szName, m_path.getName().c_str());
		int rc;

		if (AVIFileCreateStream(m_aviFile, &(m_videoStream), &(psi)))
			throw TImageException(getFilePath(), "Unable to create video stream");

		if (!m_hic) {
			if (AVIStreamSetFormat(m_videoStream, 0, &m_bitmapinfo->bmiHeader, m_bitmapinfo->bmiHeader.biSize))
				throw TImageException(getFilePath(), "unable to set format");
		} else {
			m_outputFmt = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFOHEADER) + 255 * sizeof(RGBQUAD));
			m_outputFmt->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

			rc = ICCompressGetFormat(m_hic, m_bitmapinfo, m_outputFmt);
			if (rc != ICERR_OK)
				throw TImageException(getFilePath(),
									  "Error codec (ec = " + toString(rc) + ")");

			ICCOMPRESSFRAMES icf;
			memset(&icf, 0, sizeof icf);

			icf.dwFlags = (DWORD)&icf.lKeyRate;
			icf.lStartFrame = index;
			icf.lFrameCount = 0;
			icf.lQuality = ICQUALITY_DEFAULT;
			icf.lDataRate = 0;
			icf.lKeyRate = (DWORD)(num / den);
			icf.dwRate = num;
			icf.dwScale = den;
			ICSendMessage(m_hic, ICM_COMPRESS_FRAMES_INFO, (WPARAM)&icf, sizeof(ICCOMPRESSFRAMES));

			m_maxDataSize = ICCompressGetSize(m_hic, m_bitmapinfo, m_outputFmt);

			rc = ICCompressBegin(m_hic, m_bitmapinfo, m_outputFmt);
			if (rc != ICERR_OK)
				throw TImageException(getFilePath(),
									  "Error starting codec (ec = " + toString(rc) + ")");

			if (AVIStreamSetFormat(m_videoStream, 0, &m_outputFmt->bmiHeader, m_outputFmt->bmiHeader.biSize))
				throw TImageException(getFilePath(), "unable to set format");
		}
		m_initDone = true;
	}

	// copio l'immagine nella bitmap che ho creato in createBitmap
	image->getRaster()->lock();
	void *buffin = image->getRaster()->getRawData();
	assert(buffin);

	TRasterGR8P raux;
	if (m_bpp == 24) {
		int newlx = lx * 3;
		raux = TRasterGR8P(newlx, ly);
		raux->lock();
		UCHAR *buffout24 = (UCHAR *)raux->getRawData(); //new UCHAR[newlx*ly];
		TPixel *buffin32 = (TPixel *)buffin;
		buffin = buffout24;
		for (int i = 0; i < ly; i++) {
			UCHAR *pix = buffout24 + newlx * i;
			for (int j = 0; j < lx; j++, buffin32++) {
				*pix++ = buffin32->b;
				*pix++ = buffin32->g;
				*pix++ = buffin32->r;
			}
		}
		raux->unlock();
	}
	memcpy(m_buffer, buffin, lx * ly * m_bpp / 8);
	image->getRaster()->unlock();
	raux = TRasterGR8P();

	LONG lSampWritten, lBytesWritten;

	lSampWritten = 0;
	lBytesWritten = 0;

	if (!m_hic) {
		if (AVIStreamWrite(m_videoStream, index, 1, m_buffer,
						   m_bitmapinfo->bmiHeader.biSizeImage,
						   AVIIF_KEYFRAME,
						   &lSampWritten, &lBytesWritten)) {
			throw TImageException(getFilePath(), "error writing frame");
		}
	} else {
		BITMAPINFOHEADER outHeader;
		void *bufferOut = 0;

		DWORD flagsOut = 0;
		DWORD flagsIn = !index ? ICCOMPRESS_KEYFRAME : 0;
		int res = compressFrame(&outHeader, &bufferOut, index, flagsIn, flagsOut);
		if (res != ICERR_OK) {
			if (bufferOut)
				_aligned_free(bufferOut);
			throw TImageException(getFilePath(), "error compressing frame " + toString(index));
		}

		if (outHeader.biCompression == '05xd' ||
			outHeader.biCompression == '05XD' ||
			outHeader.biCompression == 'divx' ||
			outHeader.biCompression == 'DIVX')
			if (outHeader.biSizeImage == 1 && *(char *)bufferOut == 0x7f) {
				m_delayedFrames.push_back(index);
				if (bufferOut)
					_aligned_free(bufferOut);
				return;
			}
		if (!m_delayedFrames.empty()) {
			m_delayedFrames.push_back(index);
			index = m_delayedFrames.front();
			m_delayedFrames.pop_front();
		}
		if (AVIStreamWrite(m_videoStream, index, 1, bufferOut,
						   outHeader.biSizeImage,
						   !index ? flagsOut : 0,
						   &lSampWritten, &lBytesWritten)) {
			if (bufferOut)
				_aligned_free(bufferOut);
			throw TImageException(getFilePath(), "error writing frame");
		}
		if (bufferOut)
			_aligned_free(bufferOut);
	}
}

//-----------------------------------------------------------

int TLevelWriterAvi::compressFrame(BITMAPINFOHEADER *outHeader, void **bufferOut,
								   int frameIndex, DWORD flagsIn, DWORD &flagsOut)
{
	*bufferOut = _aligned_malloc(m_maxDataSize, 128);
	*outHeader = m_outputFmt->bmiHeader;
	DWORD chunkId = 0;
	if (flagsIn)
		flagsOut = AVIIF_KEYFRAME;
	int res = ICCompress(m_hic, flagsIn,
						 outHeader, *bufferOut,
						 &m_bitmapinfo->bmiHeader, m_buffer,
						 &chunkId, &flagsOut,
						 frameIndex, frameIndex ? 0 : 0xFFFFFF,
						 0, NULL, NULL);
	return res;
}

#else

TLevelWriterAvi::TLevelWriterAvi(const TFilePath &path)
	: TLevelWriter(path)
{
}
TLevelWriterAvi::~TLevelWriterAvi() {}
TImageWriterP TLevelWriterAvi::getFrameWriter(TFrameId)
{
	throw TImageException(getFilePath(), "not supported");
	return TImageWriterP(iwa);
}
void TImageWriterAvi::save(const TImageP &) { throw TImageException(m_path, "AVI file format not supported"); }

#endif

//-----------------------------------------------------------

void TLevelWriterAvi::saveSoundTrack(TSoundTrack *st)
{
	if (!m_aviFile)
		throw TException("unable to save soundtrack");

	if (!st)
		throw TException("null reference to soundtrack");

	m_st = st; // prima devo aver salvato tutto il video, salvo l'audio alla fine!
}

//-----------------------------------------------------------

void TLevelWriterAvi::doSaveSoundTrack()
{
	WAVEFORMATEX waveinfo;
	int ret;
	LONG lSampWritten, lBytesWritten;
	int rc;

	rc = FALSE;

	AVISTREAMINFO audioStreamInfo;
	memset(&audioStreamInfo, 0, sizeof(AVISTREAMINFO));

	audioStreamInfo.fccType = streamtypeAUDIO;
	audioStreamInfo.fccHandler = 0;
	audioStreamInfo.dwFlags = 0;
	audioStreamInfo.dwCaps = 0;
	audioStreamInfo.wPriority = 0;
	audioStreamInfo.wLanguage = 0;
	audioStreamInfo.dwScale = 1;

	audioStreamInfo.dwRate = m_st->getSampleRate();

	audioStreamInfo.dwStart = 0;
	audioStreamInfo.dwLength = 0;
	audioStreamInfo.dwInitialFrames = 0;
	audioStreamInfo.dwSuggestedBufferSize = 0;
	audioStreamInfo.dwQuality = (ULONG)-1;
	audioStreamInfo.dwSampleSize = 0;
	audioStreamInfo.rcFrame.left = 0;
	audioStreamInfo.rcFrame.top = 0;
	audioStreamInfo.rcFrame.right = 0;
	audioStreamInfo.rcFrame.bottom = 0;
	audioStreamInfo.dwEditCount = 0;
	audioStreamInfo.dwFormatChangeCount = 0;
	audioStreamInfo.szName[0] = 0;

	waveinfo.wFormatTag = WAVE_FORMAT_PCM; //WAVE_FORMAT_DRM
	waveinfo.nChannels = m_st->getChannelCount();
	waveinfo.nSamplesPerSec = m_st->getSampleRate();
	waveinfo.wBitsPerSample = m_st->getBitPerSample();
	waveinfo.nBlockAlign = waveinfo.nChannels * waveinfo.wBitsPerSample >> 3;
	waveinfo.nAvgBytesPerSec = waveinfo.nSamplesPerSec * waveinfo.nBlockAlign;
	waveinfo.cbSize = sizeof(WAVEFORMATEX);

	const UCHAR *buffer = m_st->getRawData();

	if (AVIFileCreateStream(m_aviFile, &m_audioStream, &audioStreamInfo))
		throw TException("error creating soundtrack stream");

	if (AVIStreamSetFormat(m_audioStream, 0, &waveinfo, sizeof(WAVEFORMATEX)))
		throw TException("error setting soundtrack format");

	LONG count = m_st->getSampleCount();
	LONG bufSize = m_st->getSampleCount() * m_st->getSampleSize();

	if (ret = AVIStreamWrite(m_audioStream, 0, count, (char *)buffer, bufSize, AVIIF_KEYFRAME,
							 &lSampWritten, &lBytesWritten))
		throw TException("error writing soundtrack samples");
}

//===========================================================
//
//  TImageReaderAvi
//
//===========================================================

class TImageReaderAvi : public TImageReader
{

public:
	int m_frameIndex;

	TImageReaderAvi(const TFilePath &path, int index, TLevelReaderAvi *lra)
		: TImageReader(path), m_lra(lra), m_frameIndex(index)
	{
		m_lra->addRef();
	}
	~TImageReaderAvi() { m_lra->release(); }

	TImageP load() { return m_lra->load(m_frameIndex); }
	TDimension getSize() const { return m_lra->getSize(); }
	TRect getBBox() const { return TRect(); }

private:
	TLevelReaderAvi *m_lra;

	//not implemented
	TImageReaderAvi(const TImageReaderAvi &);
	TImageReaderAvi &operator=(const TImageReaderAvi &src);
};

//===========================================================
//
//  TLevelReaderAvi
//
//===========================================================

#ifdef _WIN32
TLevelReaderAvi::TLevelReaderAvi(const TFilePath &path)
	: TLevelReader(path)
#ifdef _WIN32
	  ,
	  m_srcBitmapInfo(0), m_dstBitmapInfo(0), m_hic(0), IOError(0), m_prevFrame(-1), m_decompressedBuffer(0)
#endif
{
	CoInitializeEx(0, COINIT_MULTITHREADED);

	AVIFileInit();

	int rc = AVIStreamOpenFromFileW(&m_videoStream, path.getWideString().c_str(), streamtypeVIDEO, 0, OF_READ, 0);
	if (rc != 0) {
		IOError = rc;
		throw TImageException(m_path, buildAVIExceptionString(IOError));
	}
	LONG size = sizeof(BITMAPINFO);
	m_srcBitmapInfo = (BITMAPINFO *)calloc(1, size);
	m_dstBitmapInfo = (BITMAPINFO *)calloc(1, size);

	rc = AVIStreamReadFormat(m_videoStream, 0, m_srcBitmapInfo, &size);
	if (rc)
		throw TImageException(getFilePath(), "error reading info.");

	*m_dstBitmapInfo = *m_srcBitmapInfo;
	m_dstBitmapInfo->bmiHeader.biCompression = 0;

	AVISTREAMINFO si;
	rc = AVIStreamInfo(m_videoStream, &si, sizeof(AVISTREAMINFO));
	if (rc)
		throw TImageException(getFilePath(), "error reading info.");

	m_info = new TImageInfo();
	m_info->m_frameRate = si.dwRate / double(si.dwScale);
	m_info->m_lx = si.rcFrame.right - si.rcFrame.left;
	m_info->m_ly = si.rcFrame.bottom - si.rcFrame.top;

	int bpp = m_srcBitmapInfo->bmiHeader.biBitCount;
	switch (bpp) {
	case 32: {
		m_info->m_bitsPerSample = 8;
		m_info->m_samplePerPixel = 4;
		m_dstBitmapInfo->bmiHeader.biSizeImage =
			m_dstBitmapInfo->bmiHeader.biWidth * m_dstBitmapInfo->bmiHeader.biHeight * 4;
		break;
	}
	case 24: {
		m_info->m_bitsPerSample = 8;
		m_info->m_samplePerPixel = 3;
		m_dstBitmapInfo->bmiHeader.biSizeImage =
			m_dstBitmapInfo->bmiHeader.biWidth * m_dstBitmapInfo->bmiHeader.biHeight * 3;
		break;
	}
	case 16: {
		m_info->m_bitsPerSample = 8;
		m_info->m_samplePerPixel = 2;
		//chiedo al decoder di decomprimerla in un'immagine a 24 bit (sperando che lo permetta)
		m_dstBitmapInfo->bmiHeader.biBitCount = 24;
		m_dstBitmapInfo->bmiHeader.biSizeImage =
			m_dstBitmapInfo->bmiHeader.biWidth * m_dstBitmapInfo->bmiHeader.biHeight * 3;
		break;
	}
	default: {
		throw TImageException(getFilePath(), "unable to find a decompressor for this format");
	}
	}

	Tiio::AviWriterProperties *prop = new Tiio::AviWriterProperties();
	m_info->m_properties = prop;

	if (m_srcBitmapInfo->bmiHeader.biCompression != 0) {
		//ci sono dei codec (es. Xvid) che non decomprimono bene dentro immagini a 32 bit.
		m_dstBitmapInfo->bmiHeader.biBitCount = 24;
		m_dstBitmapInfo->bmiHeader.biSizeImage =
			m_dstBitmapInfo->bmiHeader.biWidth * m_dstBitmapInfo->bmiHeader.biHeight * 3;

		ICINFO icinfo;
		memset(&icinfo, 0, sizeof(ICINFO));
		ICInfo(ICTYPE_VIDEO, si.fccHandler, &icinfo);
		m_hic = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_DECOMPRESS);
		if (!m_hic) {
			m_hic = findCandidateDecompressor();
			if (!m_hic)
				throw TImageException(getFilePath(), "unable to find a decompressor for this format");
		}

		DWORD result = ICDecompressQuery(m_hic, m_srcBitmapInfo, m_dstBitmapInfo);
		if (result != ICERR_OK)
			throw TImageException(getFilePath(), "unable to find a decompressor for this format");

		result = ICDecompressBegin(m_hic, m_srcBitmapInfo, m_dstBitmapInfo);
		if (result != ICERR_OK)
			throw TImageException(getFilePath(), "unable to initializate the decompressor");

		char descr[2048], name[2048];
		ICGetInfo(m_hic, &icinfo, sizeof(ICINFO)); // Find out the compressor name
		WideChar2Char(icinfo.szDescription, descr, sizeof(descr));
		WideChar2Char(icinfo.szName, name, sizeof(name));
		std::string compressorName;
		compressorName = std::string(name) + " '" + toString(m_dstBitmapInfo->bmiHeader.biBitCount) + "' " + std::string(descr);
		TEnumProperty *p = (TEnumProperty *)m_info->m_properties->getProperty("Codec");
		p->setValue(toWideString(compressorName));
		m_decompressedBuffer = _aligned_malloc(m_dstBitmapInfo->bmiHeader.biSizeImage, 128);
	} else {
		m_hic = 0;
		TEnumProperty *p = (TEnumProperty *)m_info->m_properties->getProperty("Codec");
		p->setValue(L"Uncompressed");
		m_decompressedBuffer = _aligned_malloc(si.dwSuggestedBufferSize, 128);
	}
}

//------------------------------------------------

TLevelReaderAvi::~TLevelReaderAvi()
{
	if (m_hic) {
		ICDecompressEnd(m_hic);
		ICClose(m_hic);
	}

	if (m_srcBitmapInfo)
		free(m_srcBitmapInfo);
	if (m_dstBitmapInfo)
		free(m_dstBitmapInfo);

	if (m_videoStream)
		AVIStreamClose(m_videoStream);

	AVIFileExit();
	_aligned_free(m_decompressedBuffer);
}

HIC TLevelReaderAvi::findCandidateDecompressor()
{
	ICINFO info = {0};
	BITMAPINFO srcInfo = *m_srcBitmapInfo;
	BITMAPINFO dstInfo = *m_dstBitmapInfo;

	for (DWORD id = 0; ICInfo(ICTYPE_VIDEO, id, &info); ++id) {
		info.dwSize = sizeof(ICINFO); // I don't think this is necessary, but just in case....

		HIC hic = ICOpen(info.fccType, info.fccHandler, ICMODE_DECOMPRESS);

		if (!hic)
			continue;

		DWORD result = ICDecompressQuery(hic, &srcInfo, &dstInfo);

		if (result == ICERR_OK) {
			// Check for a codec that doesn't actually support what it says it does.
			// We ask the codec whether it can do a specific conversion that it can't
			// possibly support.  If it does it, then we call BS and ignore the codec.
			// The Grand Tech Camera Codec and Panasonic DV codecs are known to do this.
			//
			// (general idea from Raymond Chen's blog)

			BITMAPINFOHEADER testSrc = {// note: can't be static const since IsBadWritePtr() will get called on it
										sizeof(BITMAPINFOHEADER),
										320,
										240,
										1,
										24,
										0x2E532E42,
										320 * 240 * 3,
										0,
										0,
										0,
										0};

			DWORD res = ICDecompressQuery(hic, &testSrc, NULL);

			if (ICERR_OK == res) { // Don't need to wrap this, as it's OK if testSrc gets modified.
				ICINFO info = {sizeof(ICINFO)};
				ICGetInfo(hic, &info, sizeof info);

				// Okay, let's give the codec a chance to redeem itself. Reformat the input format into
				// a plain 24-bit RGB image, and ask it what the compressed format is. If it produces
				// a FOURCC that matches, allow it to handle the format. This should allow at least
				// the codec's primary format to work. Otherwise, drop it on the ground.

				if (m_srcBitmapInfo) {
					BITMAPINFOHEADER unpackedSrc = {
						sizeof(BITMAPINFOHEADER),
						m_srcBitmapInfo->bmiHeader.biWidth,
						m_srcBitmapInfo->bmiHeader.biHeight,
						1,
						24,
						BI_RGB,
						0,
						0,
						0,
						0,
						0};

					unpackedSrc.biSizeImage = ((unpackedSrc.biWidth * 3 + 3) & ~3) * abs(unpackedSrc.biHeight);

					LONG size = ICCompressGetFormatSize(hic, &unpackedSrc);

					if (size >= sizeof(BITMAPINFOHEADER)) {
						BITMAPINFOHEADER tmp;
						if (ICERR_OK == ICCompressGetFormat(hic, &unpackedSrc, &tmp) &&
							tmp.biCompression == m_srcBitmapInfo->bmiHeader.biCompression)
							return hic;
					}
				}
			} else
				return hic;
		}
		ICClose(hic);
	}
	return NULL;
}

//-----------------------------------------------------------

TLevelP TLevelReaderAvi::loadInfo()
{
	if (IOError)
		throw TImageException(m_path, buildAVIExceptionString(IOError));
	int nFrames = AVIStreamLength(m_videoStream);
	if (nFrames == -1)
		return TLevelP();
	TLevelP level;
	for (int i = 1; i <= nFrames; i++)
		level->setFrame(i, TImageP());
	return level;
}

//-----------------------------------------------------------

TImageReaderP TLevelReaderAvi::getFrameReader(TFrameId fid)
{
	if (IOError != 0)
		throw TImageException(m_path, buildAVIExceptionString(IOError));
	if (fid.getLetter() != 0)
		return TImageReaderP(0);
	int index = fid.getNumber() - 1;

	TImageReaderAvi *ira = new TImageReaderAvi(m_path, index, this);
	return TImageReaderP(ira);
}

//------------------------------------------------------------------------------

TDimension TLevelReaderAvi::getSize()
{
	QMutexLocker sl(&m_mutex);
	AVISTREAMINFO psi;
	AVIStreamInfo(m_videoStream, &psi, sizeof(AVISTREAMINFO));
	return TDimension(psi.rcFrame.right - psi.rcFrame.left, psi.rcFrame.bottom - psi.rcFrame.top);
}

//------------------------------------------------

TImageP TLevelReaderAvi::load(int frameIndex)
{
	AVISTREAMINFO si;
	int rc = AVIStreamInfo(m_videoStream, &si, sizeof(AVISTREAMINFO));
	if (rc)
		throw TImageException(getFilePath(), "error reading info.");
	void *bufferOut = 0;

	if (!m_hic) {
		rc = readFrameFromStream(m_decompressedBuffer, si.dwSuggestedBufferSize, frameIndex);
		if (rc) {
			throw TImageException(m_path, "unable read frame " + toString(frameIndex) + "from video stream.");
		}
	} else {
		int prevKeyFrame = m_prevFrame == frameIndex - 1 ? frameIndex : getPrevKeyFrame(m_videoStream, frameIndex);
		while (prevKeyFrame <= frameIndex) {
			bufferOut = _aligned_malloc(si.dwSuggestedBufferSize, 128);
			DWORD bytesRead = si.dwSuggestedBufferSize;
			rc = readFrameFromStream(bufferOut, bytesRead, prevKeyFrame);
			if (rc) {
				_aligned_free(bufferOut);
				throw TImageException(m_path, "unable read frame " + toString(frameIndex) + "from video stream.");
			}

			DWORD res = decompressFrame(bufferOut, bytesRead, m_decompressedBuffer, prevKeyFrame, frameIndex);

			_aligned_free(bufferOut);
			if (res != ICERR_OK) {
				throw TImageException(m_path, "error decompressing frame " + toString(frameIndex));
			}
			prevKeyFrame++;
		}
	}

	int width = m_srcBitmapInfo->bmiHeader.biWidth;
	int height = m_srcBitmapInfo->bmiHeader.biHeight;

	int bpp = m_dstBitmapInfo->bmiHeader.biBitCount;
	m_prevFrame = frameIndex;
	switch (bpp) {
		CASE 32:
		{
			TRasterPT<TPixelRGBM32> ret;
			ret.create(width, height);
			ret->lock();
			memcpy(ret->getRawData(), m_decompressedBuffer, width * height * 4);
			ret->unlock();
			return TRasterImageP(ret);
		}
		CASE 24:
		{
			TRasterImageP i(DIBToRaster((UCHAR *)m_decompressedBuffer, width, height));
			return i;
		}
	DEFAULT : {
		throw TImageException(m_path, toString(bpp) + " to 32 bit not supported\n");
	}
	}
	return TRasterImageP();
}

//------------------------------------------------

int TLevelReaderAvi::readFrameFromStream(void *bufferOut, DWORD &bufferSize, int frameIndex) const
{
	assert(bufferOut && bufferSize > 0);
	LONG bytesReaded = 0;
	LONG samplesReaded = 0;

	int rc = AVIStreamRead(m_videoStream,
						   frameIndex,
						   1,
						   bufferOut,
						   bufferSize,
						   &bytesReaded,
						   &samplesReaded);
	if (!rc) {
		assert(samplesReaded == 1);				 //deve aver letto un frame!!!
		assert(bytesReaded <= (LONG)bufferSize); //deve aver letto un numero di byte
												 //minore o uguale di quello che ci aspettiamo
		bufferSize = bytesReaded;
	}
	return rc;
}

//------------------------------------------------

DWORD TLevelReaderAvi::decompressFrame(void *srcBuffer, int srcSize, void *dstBuffer, int currentFrame, int desiredFrame)
{
	BITMAPINFOHEADER srcHeader = m_srcBitmapInfo->bmiHeader;
	BITMAPINFOHEADER dstHeader = m_dstBitmapInfo->bmiHeader;
	srcHeader.biSizeImage = srcSize;

	DWORD dwFlags = 0;

	if (!isAKeyFrame(m_videoStream, currentFrame))
		dwFlags |= ICDECOMPRESS_NOTKEYFRAME;
	if (currentFrame < desiredFrame)
		dwFlags |= ICDECOMPRESS_PREROLL;

	DWORD res = ICDecompress(m_hic, dwFlags,
							 &srcHeader,
							 srcBuffer,
							 &dstHeader,
							 dstBuffer);
	return res;
}

#else
TLevelReaderAvi::TLevelReaderAvi(const TFilePath &path)::TLevelReader(path)
{
}
TLevelReaderAvi::~TLevelReaderAvi() {}
TLevelP TLevelReaderAvi::loadInfo() { return TLevelP(); }
TImageReaderP TLevelReaderAvi::getFrameReader(TFrameId fid)
{
	throw TImageException(m_path, "AVI not supported");
	return TImageReaderP(0);
}
TDimension TLevelReaderAvi::getSize() { return TDimension(); }
TImageP TLevelReaderAvi::load(int frameIndex)
{
	throw TImageException(m_path, "AVI not supported");
	return TImageP(0);
}
#endif

//===========================================================
//
//  Tiio::AviWriterProperties
//
//===========================================================

#ifdef _WIN32
Tiio::AviWriterProperties::AviWriterProperties()
	: m_codec("Codec")
{
	if (m_defaultCodec.getRange().empty()) {
		char descr[2048], name[2048];
		DWORD fccType = 0;
		ICINFO icinfo;
		BITMAPINFO inFmt;

		m_defaultCodec.addValue(L"Uncompressed");

		memset(&inFmt, 0, sizeof(BITMAPINFO));
		inFmt.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		inFmt.bmiHeader.biWidth = 100;
		inFmt.bmiHeader.biHeight = 100;
		inFmt.bmiHeader.biPlanes = 1;
		inFmt.bmiHeader.biCompression = BI_RGB;
		for (int bpp = 32; bpp >= 24; bpp -= 8) {
			inFmt.bmiHeader.biBitCount = bpp;
			for (int i = 0;; i++) {
				memset(&icinfo, 0, sizeof icinfo);
				int rc = ICInfo(fccType, i, &icinfo);
				if (!rc)
					break;
				HIC hic = 0;
#ifdef _MSC_VER
				[&](){
					__try {
						hic = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_QUERY);
					} __except (EXCEPTION_EXECUTE_HANDLER) {
					}
				}();
#else
				try {
					hic = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_QUERY);
				} catch (...) {
				}
#endif
				if (hic) {
					if (ICGetInfo(hic, &icinfo, sizeof(ICINFO)) == 0) // Find out the compressor name
					{
						ICClose(hic);
						continue;
					}
					WideChar2Char(icinfo.szDescription, descr, sizeof(descr));
					WideChar2Char(icinfo.szName, name, sizeof(name));
					if (strstr(name, "IYUV") != 0 || (strstr(name, "IR32") != 0 && bpp == 24)) {
						ICClose(hic);
						continue;
					}

					std::string compressorName;
					compressorName = std::string(name) + " '" + toString(bpp) + "' " + std::string(descr);

					if (std::string(compressorName).find("Indeo") != -1) // per il momento togliamo i codec indeo
					{
						ICClose(hic);
						continue;
					}
					if (ICCompressQuery(hic, &inFmt, NULL) != ICERR_OK) {
						ICClose(hic);
						continue; // Skip this compressor if it can't handle the format.
					}
					m_defaultCodec.addValue(toWideString(compressorName));

					if (compressorName.find("inepak") != -1)
						m_defaultCodec.setValue(toWideString(compressorName));

					ICClose(hic);
				}
			}
		}
	}
	m_codec = m_defaultCodec;
	bind(m_codec);
}

TEnumProperty Tiio::AviWriterProperties::m_defaultCodec = TEnumProperty("Codec");

#endif