Mojn,

Ostatnimi czasy interesuję się tworzeniem aplikacji typu Remote Desktop. Dzisiaj traf padł na kompresję strumienia video za pomocą kodeka xvid, no i zatrzymał mnie dziwny problem polegający na segfaulcie w kodeku (w jego części napisanej w SSE) - przy szerokości bitmapy wejściowej większej od 1020 pixeli kodek usiłuje odczytać conajmiej jeden pixel za dużo - dokładnie z adresuicc.lpInput + biIn->biSizeImage
Dzieje się to w WriteFrames->DriverProc(ICM_COMPRESS).

Nazwałem to dziwnym, ponieważ kilka dni temu (dzisiaj też) używając tego samego kodeka i interfejsu IAVIFile + IAVIStream byłem w stanie bez problemu kompresować bitmapę 1280*768 do pliku avi.

Poniższy kod jest tworem doświadczalnym, używa pliku jako transportu strumienia video jest synchroniczny - najpierw record około 5 sekund 10 FPS, później play. Jak pewnie widać - rozmawiam z kodekiem bezpośrednio.

Rozmiar bitmapy jest ustawiany w funkcji Compress -> GetSystemMetrics. Przyznam że nie wiem jakie są ograniczenia i przypuszczam że wielkie bitmapy należy dzielić, co by było absurdalne.

#include <windows.h>
#include <vfw.h>

#pragma warning(disable:4311)
#pragma warning(disable:4312)
typedef struct CODEC CODEC;
typedef LONG (__stdcall *DP)(CODEC* codec, HDRVR hdrvr, UINT msg, LONG lParam1, LONG lParam2);
DP DriverProc;

#define hdrvr (HDRVR)0

struct FILECHUNK
{
	DWORD framelen;
	DWORD dwFlags;
	DWORD ckid;
};

// file: [BITMAPINFOHEADER video]
//       [BITMAPINFOHEADER compressed]
//       [DWORD maxframelen]
//       repeat([FILECHUNK][char frame])

void Decompress();
BOOL Compress();

int main()
{
	HMODULE hMod = LoadLibrary(TEXT("xvidvfw.dll"));
	if (!hMod)
		return MessageBox(0, TEXT("Unable to load xvidvfw.dll"),0,0);

	DriverProc = (DP)GetProcAddress(hMod, "DriverProc");
	if (!DriverProc)
		return MessageBox(0, TEXT("Unable to find DriverProc function in xvidvfw.dll"),0,0);

	if (Compress())
	{
		if (MessageBox(0, TEXT("kliknij OK aby odtworzyć video"), 0, MB_TOPMOST|MB_YESNO) == IDYES)
			Decompress();
	}
	return 0;
}



// czyta dane z nośnika (pliku) i rysuje na desktopie; 10 FPS
void Decompress()
{
	ICINFO ici;
	if (!DriverProc(0, hdrvr, ICM_GETINFO, (LONG)&ici, sizeof(ici)))
		MessageBox(0, TEXT("ICM_GETINFO failed"),0,0);

	ICOPEN icop;
	icop.fccType = ici.fccType; // wg żródeł vxid - to wystarczy

	CODEC *codec = (CODEC*)DriverProc(0, hdrvr, DRV_OPEN, 0, (LONG)&icop);
	if (!codec)
	{
		MessageBox(0, TEXT("DRV_OPEN failed"),0,0);
	}
	else
	{
		DWORD cch;
		BITMAPINFO biIn; // compressed
		BITMAPINFO biOut; // uncompressed

		HANDLE hFile = CreateFile(TEXT("c:\\xvidcap.bin"), GENERIC_READ,
			FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
		// read in reversed order
		ReadFile(hFile, &biOut, sizeof(BITMAPINFOHEADER), &cch, 0);
		ReadFile(hFile, &biIn, sizeof(BITMAPINFOHEADER), &cch, 0);

		if (DriverProc(codec, hdrvr, ICM_DECOMPRESS_BEGIN, (LONG)&biIn,
			(LONG)&biOut) != ICERR_OK)
		{
			MessageBox(0, TEXT("ICM_DECOMPRESS_BEGIN failed"),0,0);
		}
		else
		{
			DWORD dwCompressBufferSize;
			ReadFile(hFile, &dwCompressBufferSize, 4, &cch, 0);

			ICDECOMPRESS icd;
			ZeroMemory(&icd, sizeof(icd));
			//icd.dwFlags  = ;
			icd.lpbiInput  = &biIn.bmiHeader;
			icd.lpInput    = VirtualAlloc(0, dwCompressBufferSize,
				MEM_COMMIT, PAGE_READWRITE);
			icd.lpbiOutput = &biOut.bmiHeader;
			//icd.lpOutput = <- CreateDIBSection
			//icd.ckid = ;
			HDC dcDesktop = GetDC(0);
			HDC cdc       = CreateCompatibleDC(dcDesktop);
			HANDLE dib    = SelectObject(cdc, CreateDIBSection(dcDesktop, &biOut,
				DIB_RGB_COLORS, &icd.lpOutput, 0, 0));

			while (1)
			{
				int time = (int)GetTickCount();
				FILECHUNK fc;

				if (!ReadFile(hFile, &fc, sizeof(fc), &cch, 0) ||
					(cch != sizeof(fc))) break;

				if (!ReadFile(hFile, icd.lpInput, fc.framelen, &cch, 0) ||
					(cch != fc.framelen)) break;

				icd.dwFlags = fc.dwFlags;
				icd.ckid    = fc.ckid;

				if (DriverProc(codec, hdrvr, ICM_DECOMPRESS, (LONG)&icd, sizeof(icd)) != ICERR_OK)
				{
					MessageBox(0, TEXT("ICM_DECOMPRESS failed"),0,0);
					break;
				}
				time = 100 - (GetTickCount() - time);
				if (time > 0) Sleep((DWORD)time);

				BitBlt(dcDesktop, 0, 0, biIn.bmiHeader.biWidth, biIn.bmiHeader.biWidth,
					cdc, 0, 0, SRCCOPY);
			}

			VirtualFree(icd.lpInput, 0, MEM_RELEASE);
			DeleteObject(SelectObject(cdc, dib));
			DeleteDC(cdc);
			ReleaseDC(0, dcDesktop);

			DriverProc(codec, hdrvr, ICM_DECOMPRESS_END, 0, 0);
		}
		DriverProc(codec, hdrvr, DRV_CLOSE, 0, (LONG)&icop);
	}
}


BOOL WriteFrames(CODEC* codec,BITMAPINFOHEADER* biIn,BITMAPINFOHEADER* biOut);


BOOL Compress()
{
	BOOL ok = false;
	ICINFO ici;
	if (!DriverProc(0, hdrvr, ICM_GETINFO, (LONG)&ici, sizeof(ici)))
		MessageBox(0, TEXT("ICM_GETINFO failed"),0,0);

	ICOPEN icop;
	icop.fccType = ici.fccType; // wg żródeł vxid - to wystarczy

	CODEC *codec = (CODEC*)DriverProc(0, hdrvr, DRV_OPEN, 0, (LONG)&icop);
	if (!codec)
	{
		MessageBox(0, TEXT("DRV_OPEN failed"),0,0);
	}
	else
	{
		if (DriverProc(codec, hdrvr, ICM_CONFIGURE, 0, 0) == ICERR_OK)
		{
			BITMAPINFO biIn;
			BITMAPINFO biOut;
			ZeroMemory(&biIn, sizeof(biIn));

			biIn.bmiHeader.biSize     = sizeof(BITMAPINFOHEADER);
			biIn.bmiHeader.biPlanes   = 1;
			biIn.bmiHeader.biWidth    = GetSystemMetrics(SM_CXSCREEN); // 1020 max
			biIn.bmiHeader.biHeight   = GetSystemMetrics(SM_CYSCREEN);
			biIn.bmiHeader.biBitCount = 24;
			biIn.bmiHeader.biSizeImage = ((biIn.bmiHeader.biWidth+3)&0xfffffffc) *
				biIn.bmiHeader.biHeight * 3;

			if (DriverProc(codec, hdrvr, ICM_COMPRESS_GET_FORMAT, (LONG)&biIn,
				(LONG)&biOut) != ICERR_OK)
			{
				MessageBox(0, TEXT("ICM_COMPRESS_GET_FORMAT failed"),0,0);
			}
			else
			{
				if (DriverProc(codec, hdrvr, ICM_COMPRESS_QUERY, (LONG)&biIn,
					(LONG)&biOut) != ICERR_OK)
					MessageBox(0, TEXT("ICM_COMPRESS_QUERY failed"),0,0);

				if (DriverProc(codec, hdrvr, ICM_COMPRESS_BEGIN,
					(LONG)&biIn.bmiHeader, (LONG)&biOut.bmiHeader) != ICERR_OK)
				{
					MessageBox(0, TEXT("ICM_COMPRESS_BEGIN failed"),0,0);
				}
				else
				{
					ok = WriteFrames(codec, &biIn.bmiHeader, &biOut.bmiHeader);
					DriverProc(codec, hdrvr, ICM_COMPRESS_END, (LONG)&biIn,
						(LONG)&biOut);
				}
			}
		}
		DriverProc(codec, hdrvr, DRV_CLOSE, 0, (LONG)&icop);
	}
	return ok;
}


BOOL WriteFrames(CODEC* codec,BITMAPINFOHEADER* biIn,BITMAPINFOHEADER* biOut)
{
	BOOL ok = true;
	FILECHUNK fc;
	DWORD dwCompressBufferSize = DriverProc(codec, hdrvr, ICM_COMPRESS_GET_SIZE,
		(LONG)biIn, (LONG)biOut);

	ICCOMPRESS icc;
	DWORD cch;

	ZeroMemory(&icc, sizeof(icc));

	icc.dwFlags    = ICCOMPRESS_KEYFRAME;
	icc.lpckid     = &fc.ckid;
	icc.lpdwFlags  = &fc.dwFlags;
	icc.lFrameNum  = 1;
	icc.dwFrameSize= biIn->biSizeImage;
	//icc.lFrameNum = <- for {}

	icc.lpbiOutput = biOut;
	icc.lpOutput   = VirtualAlloc(0, dwCompressBufferSize, MEM_COMMIT, PAGE_READWRITE);
	icc.lpbiInput  = biIn;
	//icc.lpInput  = ;  <- CreateDIBSection
	icc.lpbiPrev  = biIn;

	// niech plik będzie nośnikiem
	HANDLE hFile = CreateFile(TEXT("c:\\xvidcap.bin"), GENERIC_WRITE, FILE_SHARE_READ,
		0, CREATE_ALWAYS, 0, 0);
	WriteFile(hFile, biIn, sizeof(BITMAPINFOHEADER), &cch, 0);
	WriteFile(hFile, biOut, sizeof(BITMAPINFOHEADER), &cch, 0);
	WriteFile(hFile, &dwCompressBufferSize, 4, &cch, 0);

	HDC dcDesktop = GetDC(0);
	HDC cdc       = CreateCompatibleDC(dcDesktop);

//FIX	biIn->biHeight++;
	HANDLE dib    = SelectObject(cdc, CreateDIBSection(dcDesktop, (BITMAPINFO*)biIn,
		DIB_RGB_COLORS, (void**)&icc.lpInput, 0, 0));

//FIX	biIn->biHeight--;

	for (icc.lFrameNum=0; icc.lFrameNum<50; icc.lFrameNum++)
	{
		int time = (int)GetTickCount();
		BitBlt(cdc, 0, 0, biIn->biWidth, biIn->biHeight,
			dcDesktop, 0, 0, SRCCOPY | CAPTUREBLT);

		icc.lpbiOutput->biSizeImage = dwCompressBufferSize;
		__try
		{
			if (DriverProc(codec, hdrvr, ICM_COMPRESS, (LONG)&icc, sizeof(icc)) != ICERR_OK)
			{
				ok = false;
				MessageBox(0, TEXT("ICM_COMPRESS failed"),0,0);
				break;
			}
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			ok = false;
			MessageBox(0, TEXT("exception in ICM_COMPRESS"),0,0);
			break;
		}

		// compressed: icc.lpOutput
		// size:       icc.lpbiOutput->biSizeImage
		fc.framelen = icc.lpbiOutput->biSizeImage;
		WriteFile(hFile, &fc, sizeof(fc), &cch, 0);

		WriteFile(hFile, icc.lpOutput, icc.lpbiOutput->biSizeImage, &cch, 0);
		time = 100 - (GetTickCount() - time);
		if (time > 0) Sleep((DWORD)time);
	}
	CloseHandle(hFile);

	VirtualFree(icc.lpOutput, 0, MEM_RELEASE);

	DeleteObject(SelectObject(cdc, dib));
	DeleteDC(cdc);
	ReleaseDC(0, dcDesktop);
	return ok;
}

EDIT: CreateDIBSection z jedną zapasową linią załatwia sprawę. Identyczny bug jest w 3ivxVfWCodec.dll.
Poprawki dodałem jako //FIX.