mirror of
https://github.com/dbalsom/x86_microcode.git
synced 2026-06-09 13:04:17 +03:00
333 lines
11 KiB
C++
333 lines
11 KiB
C++
#include "alfe/main.h"
|
|
|
|
#ifndef INCLUDED_AVI_H
|
|
#define INCLUDED_AVI_H
|
|
|
|
#include "alfe/com.h"
|
|
#include "alfe/bitmap.h"
|
|
#include "alfe/rational.h"
|
|
#include <Vfw.h>
|
|
|
|
String aviErrorMessage(HRESULT hr)
|
|
{
|
|
switch (hr) {
|
|
case AVIERR_UNSUPPORTED: return "Unsupported";
|
|
case AVIERR_BADFORMAT: return "Bad format";
|
|
case AVIERR_MEMORY: return "Out of memory";
|
|
case AVIERR_INTERNAL: return "Internal error";
|
|
case AVIERR_BADFLAGS: return "Bad flags";
|
|
case AVIERR_BADPARAM: return "Bad parameters";
|
|
case AVIERR_BADSIZE: return "Bad size";
|
|
case AVIERR_BADHANDLE: return "Bad AVIFile handle";
|
|
case AVIERR_FILEREAD: return "File read error";
|
|
case AVIERR_FILEWRITE: return "File write error";
|
|
case AVIERR_FILEOPEN: return "File open error";
|
|
case AVIERR_COMPRESSOR: return "Compressor error";
|
|
case AVIERR_NOCOMPRESSOR: return "Compressor not available";
|
|
case AVIERR_READONLY: return "File marked read-only";
|
|
case AVIERR_NODATA: return "No data";
|
|
case AVIERR_BUFFERTOOSMALL: return "Buffer too small";
|
|
case AVIERR_CANTCOMPRESS: return "Can't compress";
|
|
case AVIERR_USERABORT: return "Aborted by user";
|
|
}
|
|
return "Error code: " + hex(hr, 8);
|
|
}
|
|
|
|
#define IF_AVI_ERROR_THROW(expr) CODE_MACRO( \
|
|
HRESULT hrMacro = (expr); \
|
|
IF_TRUE_THROW(FAILED(hrMacro), Exception(aviErrorMessage(hrMacro))); \
|
|
)
|
|
|
|
class AVIFileInitializer : Uncopyable
|
|
{
|
|
public:
|
|
AVIFileInitializer() { AVIFileInit(); }
|
|
~AVIFileInitializer() { AVIFileExit(); }
|
|
};
|
|
|
|
class AVIFile : Uncopyable
|
|
{
|
|
public:
|
|
AVIFile(File file)
|
|
{
|
|
NullTerminatedWideString inputPathWide(file.path());
|
|
IF_AVI_ERROR_THROW(
|
|
AVIFileOpen(&_aviFile, inputPathWide, OF_READ, NULL));
|
|
}
|
|
~AVIFile() { AVIFileRelease(_aviFile); }
|
|
void getStream(IAVIStream** aviStream, DWORD fccType, LONG lParam)
|
|
{
|
|
IF_AVI_ERROR_THROW(_aviFile->GetStream(aviStream, fccType, lParam));
|
|
}
|
|
//AVIFILEINFO aviFileInfo;
|
|
//ZeroMemory(&aviFileInfo, sizeof(AVIFILEINFO));
|
|
//IF_AVI_ERROR_THROW(aviFile->Info(&aviFileInfo, sizeof(AVIFILEINFO)));
|
|
//int width = aviFileInfo.dwWidth;
|
|
//int height = aviFileInfo.dwHeight;
|
|
private:
|
|
IAVIFile* _aviFile;
|
|
};
|
|
|
|
class AVIStream
|
|
{
|
|
public:
|
|
AVIStream(AVIFile* aviFile, DWORD fccType = streamtypeVIDEO,
|
|
LONG lParam = 0)
|
|
{
|
|
aviFile->getStream(&_aviStream, fccType, lParam);
|
|
ZeroMemory(&_aviStreamInfo, sizeof(AVISTREAMINFO));
|
|
IF_AVI_ERROR_THROW(_aviStream->Info(&_aviStreamInfo,
|
|
sizeof(AVISTREAMINFO)));
|
|
|
|
if (_aviStreamInfo.fccType != streamtypeVIDEO)
|
|
throw Exception("Not a video stream");
|
|
|
|
LONG cbFormat = 0;
|
|
_aviStream->ReadFormat(_frame, 0, &cbFormat);
|
|
_formatBuffer.ensure(cbFormat);
|
|
IF_AVI_ERROR_THROW(_aviStream->ReadFormat(_frame, &_formatBuffer[0],
|
|
&cbFormat));
|
|
_bitmapInfoHeader =
|
|
*reinterpret_cast<BITMAPINFOHEADER*>(&_formatBuffer[0]);
|
|
}
|
|
PGETFRAME getFrameOpen()
|
|
{
|
|
BITMAPINFOHEADER header = _bitmapInfoHeader;
|
|
header.biSize = sizeof(BITMAPINFOHEADER);
|
|
header.biCompression = BI_RGB;
|
|
header.biBitCount = 24;
|
|
return AVIStreamGetFrameOpen(_aviStream, &header);
|
|
}
|
|
BITMAPINFOHEADER bitmapInfoHeader() { return _bitmapInfoHeader; }
|
|
int frames() { return _aviStreamInfo.dwLength; }
|
|
private:
|
|
COMPointer<IAVIStream> _aviStream;
|
|
AVISTREAMINFO _aviStreamInfo;
|
|
BITMAPINFOHEADER _bitmapInfoHeader;
|
|
PCMWAVEFORMAT _pcmWaveFormat;
|
|
LONG _frame;
|
|
//Array<Byte> _buffer;
|
|
Array<Byte> _formatBuffer;
|
|
};
|
|
|
|
class GetFrame : Uncopyable
|
|
{
|
|
public:
|
|
GetFrame(AVIStream* aviStream) : _getFrame(aviStream->getFrameOpen())
|
|
{
|
|
_bitmapInfoHeader = aviStream->bitmapInfoHeader();
|
|
_frames = aviStream->frames();
|
|
_frame = 0;
|
|
}
|
|
Bitmap<SRGB> getFrame(Bitmap<SRGB> bitmap)
|
|
{
|
|
int height = _bitmapInfoHeader.biHeight;
|
|
if (height < 0)
|
|
height = -height;
|
|
//if (_bitmapInfoHeader.biCompression != BI_RGB)
|
|
// throw Exception("Don't know how to decode this pixel type yet");
|
|
Vector s(_bitmapInfoHeader.biWidth, height);
|
|
//_buffer.ensure(s.x * s.y * _bitmapInfoHeader.biBitCount / 8);
|
|
bitmap.ensure(s);
|
|
//LONG lBytes;
|
|
//LONG lSamples;
|
|
//IF_AVI_ERROR_THROW(_aviStream->Read(_frame, 1, &_buffer[0],
|
|
// s.x * s.y * 4, &lBytes, &lSamples));
|
|
//if (lBytes > s.x * s.y * 4)
|
|
// throw Exception("Frame too large");
|
|
//if (lSamples != 1)
|
|
// throw Exception("Didn't get 1 frame");
|
|
|
|
Byte* sl = static_cast<Byte*>(_getFrame->GetFrame(_frame)) +
|
|
sizeof(BITMAPINFOHEADER);
|
|
|
|
if (_bitmapInfoHeader.biBitCount == 24) {
|
|
//auto sl = &_buffer[0];
|
|
int ss = _bitmapInfoHeader.biWidth * 3;
|
|
if (_bitmapInfoHeader.biHeight > 0) {
|
|
sl += ss * (s.y - 1);
|
|
ss = -ss;
|
|
}
|
|
auto dl = bitmap.data();
|
|
for (int y = 0; y < s.y; ++y) {
|
|
auto p = reinterpret_cast<SRGB*>(sl);
|
|
auto d = reinterpret_cast<SRGB*>(dl);
|
|
for (int x = 0; x < s.x; ++x) {
|
|
*d = *p;
|
|
++p;
|
|
++d;
|
|
}
|
|
sl += ss;
|
|
dl += bitmap.stride();
|
|
}
|
|
}
|
|
else {
|
|
if (_bitmapInfoHeader.biBitCount != 32) {
|
|
throw Exception(
|
|
"Don't know how to decode this pixel size yet");
|
|
}
|
|
//auto sl = &_buffer[0];
|
|
int ss = _bitmapInfoHeader.biWidth * 4;
|
|
if (_bitmapInfoHeader.biHeight > 0) {
|
|
sl += ss * (s.y - 1);
|
|
ss = -ss;
|
|
}
|
|
auto dl = bitmap.data();
|
|
for (int y = 0; y < s.y; ++y) {
|
|
auto p = reinterpret_cast<DWORD*>(sl);
|
|
auto d = reinterpret_cast<SRGB*>(dl);
|
|
for (int x = 0; x < s.x; ++x) {
|
|
DWORD v = *p;
|
|
*d = SRGB((v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff);
|
|
++p;
|
|
++d;
|
|
}
|
|
sl += ss;
|
|
dl += bitmap.stride();
|
|
}
|
|
}
|
|
++_frame;
|
|
return bitmap;
|
|
}
|
|
bool atEnd() { return _frame == _frames; }
|
|
~GetFrame() { AVIStreamGetFrameClose(_getFrame); }
|
|
private:
|
|
PGETFRAME _getFrame;
|
|
BITMAPINFOHEADER _bitmapInfoHeader;
|
|
int _frames;
|
|
int _frame;
|
|
};
|
|
|
|
class AVIOptions
|
|
{
|
|
public:
|
|
AVIOptions(HWND hParent, IAVIStream** videoStream)
|
|
: _hParent(hParent), _videoStream(videoStream)
|
|
{
|
|
ZeroMemory(&_opts, sizeof(_opts));
|
|
_opts.fccHandler = mmioFOURCC('u', 'l', 'r', 'g');
|
|
_opts.dwFlags = AVICOMPRESSF_VALID;
|
|
_opts.cbParms = 0; // 4;
|
|
}
|
|
void showOptions()
|
|
{
|
|
_aopts[0] = &_opts;
|
|
IF_FALSE_THROW(
|
|
AVISaveOptions(_hParent, 0, 1, _videoStream, _aopts) == TRUE);
|
|
}
|
|
AVICOMPRESSOPTIONS* options() { return _aopts[0]; }
|
|
~AVIOptions() { AVISaveOptionsFree(1, _aopts); }
|
|
private:
|
|
AVICOMPRESSOPTIONS _opts;
|
|
AVICOMPRESSOPTIONS* _aopts[1];
|
|
HWND _hParent;
|
|
IAVIStream** _videoStream;
|
|
};
|
|
|
|
class AVIWriter
|
|
{
|
|
public:
|
|
AVIWriter(File file, Vector size, Rational frameRate,
|
|
/*const WAVEFORMATEX* waveFormatEx,*/
|
|
HWND hParent)
|
|
{
|
|
BITMAPINFO bi;
|
|
ZeroMemory(&bi, sizeof(bi));
|
|
BITMAPINFOHEADER& bih = bi.bmiHeader;
|
|
bih.biSize = sizeof(bih);
|
|
bih.biWidth = size.x;
|
|
bih.biHeight = size.y;
|
|
bih.biPlanes = 1;
|
|
bih.biBitCount = 24;
|
|
bih.biCompression = BI_RGB;
|
|
_bytesPerLine = (bih.biWidth * bih.biBitCount / 8 + 3) & -4;
|
|
bih.biSizeImage = _bytesPerLine * size.y;
|
|
bih.biXPelsPerMeter = 10000;
|
|
bih.biYPelsPerMeter = 10000;
|
|
bih.biClrUsed = 0;
|
|
bih.biClrImportant = 0;
|
|
|
|
HDC hdcScreen = GetDC(0);
|
|
_hdc = CreateCompatibleDC(hdcScreen);
|
|
ReleaseDC(0, hdcScreen);
|
|
|
|
_hBitmap = CreateDIBSection(_hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
|
|
&_bits, NULL, NULL);
|
|
IF_ZERO_THROW(_hBitmap);
|
|
IF_ZERO_THROW(GetObject(_hBitmap, sizeof(_dibSection), &_dibSection));
|
|
|
|
NullTerminatedWideString w(file.path());
|
|
IF_AVI_ERROR_THROW(
|
|
AVIFileOpen(&_pfile, w, OF_WRITE | OF_CREATE, NULL));
|
|
//if (waveFormatEx == NULL)
|
|
// ZeroMemory(&_waveFormatEx, sizeof(WAVEFORMATEX));
|
|
//else
|
|
// CopyMemory(&_waveFormatEx, waveFormatEx, sizeof(WAVEFORMATEX));
|
|
//_audioStream = 0;
|
|
_frames = 0;
|
|
//_samples = 0;
|
|
|
|
AVISTREAMINFO strhdr;
|
|
ZeroMemory(&strhdr, sizeof(strhdr));
|
|
strhdr.fccType = streamtypeVIDEO;
|
|
strhdr.fccHandler = 0;
|
|
strhdr.dwRate = frameRate.numerator;
|
|
strhdr.dwScale = frameRate.denominator;
|
|
strhdr.dwSuggestedBufferSize = _dibSection.dsBmih.biSizeImage;
|
|
SetRect(&strhdr.rcFrame, 0, 0, _dibSection.dsBmih.biWidth,
|
|
_dibSection.dsBmih.biHeight);
|
|
IF_AVI_ERROR_THROW(
|
|
AVIFileCreateStream(_pfile, &_videoStream, &strhdr));
|
|
|
|
{
|
|
AVIOptions options(hParent, &_videoStream);
|
|
options.showOptions();
|
|
IF_AVI_ERROR_THROW(AVIMakeCompressedStream(&_compressedStream,
|
|
_videoStream, options.options(), NULL));
|
|
}
|
|
IF_AVI_ERROR_THROW(AVIStreamSetFormat(_compressedStream, 0,
|
|
&_dibSection.dsBmih, _dibSection.dsBmih.biSize +
|
|
_dibSection.dsBmih.biClrUsed * sizeof(RGBQUAD)));
|
|
}
|
|
~AVIWriter()
|
|
{
|
|
//if (_audioStream != 0)
|
|
// AVIStreamRelease(_audioStream);
|
|
if (_compressedStream != 0)
|
|
AVIStreamRelease(_compressedStream);
|
|
if (_videoStream != 0)
|
|
AVIStreamRelease(_videoStream);
|
|
if (_pfile != 0)
|
|
AVIFileRelease(_pfile);
|
|
|
|
DeleteDC(_hdc);
|
|
DeleteObject(_hBitmap);
|
|
}
|
|
|
|
void AddAviFrame()
|
|
{
|
|
IF_AVI_ERROR_THROW(AVIStreamWrite(_compressedStream, _frames, 1,
|
|
_dibSection.dsBm.bmBits, _dibSection.dsBmih.biSizeImage,
|
|
AVIIF_KEYFRAME, NULL, NULL));
|
|
++_frames;
|
|
}
|
|
Byte* bits() { return static_cast<Byte*>(_bits); }
|
|
int stride() { return _bytesPerLine; }
|
|
|
|
private:
|
|
IAVIFile* _pfile;
|
|
//WAVEFORMATEX _waveFormatEx;
|
|
//IAVIStream* _audioStream;
|
|
IAVIStream* _videoStream;
|
|
IAVIStream* _compressedStream;
|
|
int _frames;
|
|
//int _samples;
|
|
HDC _hdc;
|
|
HBITMAP _hBitmap;
|
|
void* _bits;
|
|
DIBSECTION _dibSection;
|
|
int _bytesPerLine;
|
|
};
|
|
|
|
#endif // INCLUDED_AVI_H
|