openclonk/standard/src/StdVideo.cpp

337 lines
10 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, 2007 Matthes Bender
* Copyright (c) 2005 Günther Brammer
* Copyright (c) 2006 Sven Eberhardt
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* Some functions to help with saving AVI files using Video for Windows */
#ifdef _WIN32
#include <Standard.h>
#include <StdVideo.h>
#include <StdSurface2.h>
#include <windows.h>
BOOL AVIOpenOutput(const char *szFilename,
PAVIFILE *ppAviFile,
PAVISTREAM *ppAviStream,
int iWidth, int iHeight)
{
// Init AVI system
AVIFileInit();
// Create avi file
if ( AVIFileOpen(
ppAviFile,
szFilename,
OF_CREATE | OF_WRITE,
NULL) != 0)
{
return FALSE;
}
// Create stream
AVISTREAMINFO avi_info;
RECT frame; frame.left=0; frame.top=0; frame.right=iWidth; frame.bottom=iHeight;
avi_info.fccType= streamtypeVIDEO;
avi_info.fccHandler= mmioFOURCC('M','S','V','C');
avi_info.dwFlags= 0;
avi_info.dwCaps= 0;
avi_info.wPriority= 0;
avi_info.wLanguage= 0;
avi_info.dwScale= 1;
avi_info.dwRate= 35;
avi_info.dwStart= 0;
avi_info.dwLength= 10; // ??
avi_info.dwInitialFrames= 0;
avi_info.dwSuggestedBufferSize= 0;
avi_info.dwQuality= -1;
avi_info.dwSampleSize= 0;
avi_info.rcFrame= frame;
avi_info.dwEditCount= 0;
avi_info.dwFormatChangeCount= 0;
SCopy("MyRecording",avi_info.szName);
if ( AVIFileCreateStream(
*ppAviFile,
ppAviStream,
&avi_info) != 0)
{
return FALSE;
}
return TRUE;
}
BOOL AVICloseOutput(PAVIFILE *ppAviFile,
PAVISTREAM *ppAviStream)
{
if (ppAviStream && *ppAviStream)
{ AVIStreamRelease(*ppAviStream); *ppAviStream=NULL; }
if (ppAviFile && *ppAviFile)
{ AVIFileRelease(*ppAviFile); *ppAviFile=NULL; }
return TRUE;
}
BOOL AVIPutFrame(PAVISTREAM pAviStream,
long lFrame,
void *lpInfo, long lInfoSize,
void *lpData, long lDataSize)
{
long lBytesWritten=0,lSamplesWritten=0;
AVIStreamSetFormat(
pAviStream,
lFrame,
lpInfo,
lInfoSize
);
if (AVIStreamWrite(
pAviStream,
lFrame,
1,
lpData,
lDataSize,
AVIIF_KEYFRAME,
&lSamplesWritten,
&lBytesWritten) != 0) return FALSE;
return TRUE;
}
BOOL AVIOpenGrab(const char *szFilename,
PAVISTREAM *ppAviStream,
PGETFRAME *ppGetFrame,
int &rAviLength, int &rFrameWdt, int &rFrameHgt,
int &rFrameBitsPerPixel, int &rFramePitch)
{
// Open avi stream
if ( AVIStreamOpenFromFile(
ppAviStream,
szFilename,
streamtypeVIDEO,
0,
OF_READ,
NULL) != 0) return FALSE;
// Get stream info
AVISTREAMINFO avi_info;
AVIStreamInfo(*ppAviStream,&avi_info,sizeof(avi_info));
rAviLength=avi_info.dwLength;
// Open get frame
if (!(*ppGetFrame = AVIStreamGetFrameOpen(*ppAviStream,NULL))) return FALSE;
// Get sample frame
void *pframe;
if (!(pframe = AVIStreamGetFrame(*ppGetFrame,0))) return FALSE;
// Assign sample bmp info
BITMAPINFOHEADER *sample = (BITMAPINFOHEADER*) pframe;
rFrameWdt = sample->biWidth;
rFrameHgt = sample->biHeight;
rFrameBitsPerPixel = sample->biBitCount;
rFramePitch = DWordAligned(rFrameWdt*rFrameBitsPerPixel/8);
return TRUE;
}
void AVICloseGrab(PAVISTREAM *ppAviStream,
PGETFRAME *ppGetFrame)
{
if (ppGetFrame && *ppGetFrame)
{ AVIStreamGetFrameClose(*ppGetFrame); *ppGetFrame=NULL; }
if (ppAviStream && *ppAviStream)
{ AVIStreamRelease(*ppAviStream); *ppAviStream=NULL; }
}
// ----------------------------------------
CStdAVIFile::CStdAVIFile()
: pStream(NULL), pGetFrame(NULL), hOutDib(NULL), hBitmap(NULL), hDD(NULL), hWnd(NULL), hDC(NULL), pbmi(NULL),
iAudioBufferLength(0), pAudioData(NULL), pAudioStream(NULL), pAudioInfo(NULL), pAVIFile(NULL)
{
AVIFileInit();
}
CStdAVIFile::~CStdAVIFile()
{
Clear();
AVIFileExit();
}
void CStdAVIFile::Clear()
{
// free any stuff
CloseAudioStream();
if (hBitmap) { DeleteObject(hBitmap); hBitmap = NULL; }
if (hDD) { DrawDibClose(hDD); hDD = NULL; }
if (hDC) { ReleaseDC(hWnd, hDC); hDC = NULL; }
if (pGetFrame) { AVIStreamGetFrameClose(pGetFrame); pGetFrame=NULL; }
if (pStream) { AVIStreamRelease(pStream); pStream = NULL; }
if (pbmi) { delete [] pbmi; pbmi = NULL; }
if (pAVIFile) { AVIFileRelease(pAVIFile); pAVIFile = NULL; }
sFilename.Clear();
}
bool CStdAVIFile::OpenFile(const char *szFilename, HWND hWnd, int32_t iOutBitDepth)
{
// clear previous
Clear();
sFilename.Copy(szFilename);
// open the AVI file
if (AVIFileOpen(&pAVIFile, szFilename, OF_READ, NULL))
return false;
if (AVIFileGetStream(pAVIFile, &pStream, streamtypeVIDEO, 0))
return false;
// get stream information
AVIStreamInfo(pStream, &StreamInfo, sizeof(AVISTREAMINFO));
iWdt = StreamInfo.rcFrame.right-StreamInfo.rcFrame.left;
iHgt = StreamInfo.rcFrame.bottom-StreamInfo.rcFrame.top;
iFinalFrame = AVIStreamLength(pStream);
// some safety
if (iWdt<=0 || iHgt<=0 || iFinalFrame<=0) return false;
// calculate playback speed
iTimePerFrame = AVIStreamSampleToTime(pStream,iFinalFrame)/iFinalFrame;
// init buffer bitmap info
pbmi = (BITMAPINFO *) new BYTE[sizeof (BITMAPINFO) + 3*sizeof(RGBQUAD)];
ZeroMemory(pbmi, sizeof (BITMAPINFO) + 3*sizeof(RGBQUAD));
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = iOutBitDepth;
pbmi->bmiHeader.biWidth = iWdt;
pbmi->bmiHeader.biHeight = -iHgt;
pbmi->bmiHeader.biCompression = (iOutBitDepth == 16) ? BI_BITFIELDS : BI_RGB;
if (iOutBitDepth == 16)
{
*(DWORD*)(&(pbmi->bmiColors[2])) = 0x00f;
*(DWORD*)(&(pbmi->bmiColors[1])) = 0x0f0;
*(DWORD*)(&(pbmi->bmiColors[0])) = 0xf00;
}
hDC = CreateCompatibleDC(NULL);
if (!hDC) return false;
hDD = DrawDibOpen();
if (!hDD) return false;
hBitmap = CreateDIBSection(hDC, pbmi, DIB_RGB_COLORS, (void**)(&pFrameData), NULL, NULL);
if (!hBitmap) return false;
SelectObject(hDC, hBitmap);
// create a GetFrame-object
pGetFrame=AVIStreamGetFrameOpen(pStream, NULL /*&(pbmi->bmiHeader)*/);
if (!pGetFrame) return false;
// done, success!
return true;
}
bool CStdAVIFile::GetFrameByTime(time_t iTime, int32_t *piFrame)
{
// safeties
if (iTime < 0) return false;
if (!piFrame || !iTimePerFrame) return false;
// get frame
int iFrame = *piFrame = int32_t((iTime + (iTimePerFrame/2)) / iTimePerFrame);
return iFrame < iFinalFrame;
}
bool CStdAVIFile::GrabFrame(int32_t iFrame, CSurface *sfc) const
{
// safeties
if (!pGetFrame || !sfc) return false;
if (iFrame<0 || iFrame >= iFinalFrame) return false;
// grab desired frame
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(pGetFrame, iFrame);
// calculate actual data position
BYTE *pImageData = (BYTE *)lpbi + lpbi->biSize + lpbi->biClrUsed*sizeof(RGBQUAD);
// draw into buffer bitmap
if (!DrawDibDraw (hDD, hDC, 0, 0, iWdt, iHgt, lpbi, pImageData, 0, 0, iWdt, iHgt, 0)) return false;
// copy from buffer bitmap into surface - assumes surface is created in the correct size!
if (!sfc->Lock()) return false;
if (!sfc->CopyBytes(pFrameData)) return false;
return !!sfc->Unlock();
}
bool CStdAVIFile::OpenAudioStream()
{
// close previous
CloseAudioStream();
// open new
if (!pAVIFile) return false;
if (AVIFileGetStream(pAVIFile, &pAudioStream, streamtypeAUDIO, 0))
return false;
// get audio stream format
if (AVIStreamReadFormat(pAudioStream, AVIStreamStart(pAudioStream), NULL, &iAudioInfoLength)) return false;
if (iAudioInfoLength<sizeof(WAVEFORMAT)) return false;
pAudioInfo = (WAVEFORMAT *) new BYTE[iAudioInfoLength];
if (AVIStreamReadFormat(pAudioStream, AVIStreamStart(pAudioStream), pAudioInfo, &iAudioInfoLength))
{ delete [] pAudioInfo; pAudioInfo=NULL; return false; }
// done!
return true;
}
BYTE *CStdAVIFile::GetAudioStreamData(size_t *piStreamLength)
{
// returning the complete audio stream at once here - not very efficient, but easy...
// get stream size
if (!pAudioInfo) return NULL;
if(AVIStreamRead(pAudioStream, 0, AVIStreamLength(pAudioStream), NULL, 0, &iAudioDataLength, NULL)) return NULL;
if (iAudioDataLength<=0) return NULL;
// make sure current audio data buffer is large enoiugh to hold the data
// preceding return data with the RIFF+waveformat structure here, so it can be easily loaded by fmod
uint32_t iHeaderLength = iAudioInfoLength + sizeof(FOURCC) * 4 + 3 * sizeof(uint32_t);
LONG iReturnDataLength = iAudioDataLength + iHeaderLength;
if (iAudioBufferLength < iReturnDataLength)
{
delete [] pAudioData;
pAudioData = new BYTE[iAudioBufferLength = iReturnDataLength];
// build wave file header
BYTE *pWrite = pAudioData;
*((FOURCC *)pWrite) = mmioFOURCC('R', 'I', 'F', 'F'); pWrite += sizeof(FOURCC);
*((uint32_t *)pWrite) = iReturnDataLength - sizeof(FOURCC) - sizeof(uint32_t); pWrite += sizeof(uint32_t);
*((FOURCC *)pWrite) = mmioFOURCC('W', 'A', 'V', 'E'); pWrite += sizeof(FOURCC);
*((FOURCC *)pWrite) = mmioFOURCC('f', 'm', 't', ' '); pWrite += sizeof(FOURCC);
*((uint32_t *)pWrite) = iAudioInfoLength; pWrite += sizeof(uint32_t);
memcpy(pWrite, pAudioInfo, iAudioInfoLength); pWrite += iAudioInfoLength;
*((FOURCC *)pWrite) = mmioFOURCC('d', 'a', 't', 'a'); pWrite += sizeof(FOURCC);
*((uint32_t *)pWrite) = iAudioDataLength;
}
// get it
if(AVIStreamRead(pAudioStream, 0, AVIStreamLength(pAudioStream), pAudioData+iHeaderLength, iAudioDataLength, NULL, NULL)) return NULL;
// got the data successfully!
*piStreamLength = iReturnDataLength;
return pAudioData;
}
void CStdAVIFile::CloseAudioStream()
{
if (pAudioStream) { AVIStreamRelease(pAudioStream); pAudioStream = NULL; }
if (pAudioData) { delete [] pAudioData; pAudioData = NULL; }
if (pAudioInfo) { delete [] pAudioInfo; pAudioInfo = NULL; }
iAudioBufferLength = 0;
}
#endif // _WIN32