Professional Documents
Culture Documents
GLAVIRecorder
GLAVIRecorder
interface
{$i GLScene.inc}
{$IFDEF LINUX}{$Message Error 'Unit not supported'}{$ENDIF LINUX}
{$NOINCLUDE vfw}
{$ifdef GLS_DELPHI_6_UP}
{$HPPEMIT '#include <vfw_BCB.hpp>'}
{$else}
{$HPPEMIT '#include <vfw_BCB5.hpp>'}
{$endif}
type
TAVICompressor = (acDefault, acShowDialog, acDivX);
PAVIStream = ^IAVIStream;
// TAVISizeRestriction
//
{: Frame size restriction.<p>
Forces frame dimensions to be a multiple of 2, 4, or 8. Some compressors
require this. e.g. DivX 5.2.1 requires mutiples of 2. }
TAVISizeRestriction = ( srNoRestriction, srForceBlock2x2,
srForceBlock4x4, srForceBlock8x8);
// TAVIImageRetrievalMode
//
{: Image retrieval mode for frame capture.<p>
Following modes are supported:<p>
<li>irmSnapShot : retrieve OpenGL framebuffer content using glReadPixels
<li>irmRenderToBitmap : renders the whole scene to a bitmap, this is
the slowest mode, but it won't be affected by driver-side specifics.
<li>irmBitBlt : tranfers the framebuffer using the BitBlt function,
usually the fastest solution
</ul> }
TAVIImageRetrievalMode = (irmSnapShot, irmRenderToBitmap, irmBitBlt);
// TAVIRecorder
//
{: Component to make it easy to record GLScene frames into an AVI file. }
TAVIRecorder = class (TComponent)
private
{ Private Declarations }
AVIBitmap : TBitmap;
AVIFrameIndex : integer;
AVI_DPI : integer;
asi : TAVIStreamInfoA;
pfile : IAVIFile;
Stream, Stream_c : IAVIStream; // AVI stream and stream to be compressed
FBitmapInfo : PBitmapInfoHeader;
FBitmapBits : Pointer;
FBitmapSize : Dword;
FAVIFilename : string;
FFPS: byte;
FWidth : integer;
FHeight : integer;
FSizeRestriction : TAVISizeRestriction;
FImageRetrievalMode : TAVIImageRetrievalMode;
RecorderState : TAVIRecorderState;
FOnPostProcessEvent : TAVIRecorderPostProcessEvent;
FBuffer : TGLSceneBuffer;
protected
{ Protected Declarations }
// Now, TAVIRecorder is tailored for GLScene. Maybe we should make a generic
// TAVIRecorder, and then sub-class it to use with GLScene
FGLSceneViewer : TGLSceneViewer;
// FGLNonVisualViewer accepts GLNonVisualViewer and GLFullScreenViewer
FGLNonVisualViewer: TGLNonVisualViewer;
// FCompressor determines if the user is to choose a compressor via a dialog
box, or
// just use a default compressor without showing a dialog box.
FCompressor : TAVICompressor;
// some video compressor assumes input dimensions to be multiple of 2, 4 or
8.
// Restricted() is for rounding off the width and height.
// Currently I can't find a simple way to know which compressor imposes
// what resiction, so the SizeRestiction property is there for the user to
set.
// The source code of VirtualDub (http://www.virtualdub.org/)
// may give us some cues on this.
// ( BTW, VirtualDub is an excellent freeware for editing your AVI. For
// converting AVI into MPG, try AVI2MPG1 - http://www.mnsi.net/~jschlic1 )
function Restricted(s:integer):integer;
procedure InternalAddAVIFrame;
public
{ Public Declarations }
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
published
{ Published Declarations }
property FPS : byte read FFPS write FFPS default 25;
property GLSceneViewer : TGLSceneViewer read FGLSceneViewer write
SetGLSceneViewer;
property GLNonVisualViewer : TGLNonVisualViewer read FGLNonVisualViewer
write SetGLNonVisualViewer;
property Width : integer read FWidth write SetWidth;
property Height : integer read FHeight write SetHeight;
property Filename : String read FAVIFilename write FAVIFilename;
property Compressor : TAVICompressor read FCompressor write FCompressor
default acDefault;
property SizeRestriction : TAVISizeRestriction read FSizeRestriction write
SetSizeRestriction default srForceBlock8x8;
property ImageRetrievalMode : TAVIImageRetrievalMode read
FImageRetrievalMode write FImageRetrievalMode default irmBitBlt;
end;
procedure Register;
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
implementation
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
procedure Register;
begin
RegisterComponents('GLScene Utils', [TAVIRecorder]);
end;
// InternalGetDIB
//
function InternalGetDIB(bitmap : HBITMAP; var bitmapInfo; var bits) : Boolean;
var
focus : HWND;
dc : HDC;
errCode : Integer;
begin
InitializeBitmapInfoHeader(bitmap, TBitmapInfoHeader(bitmapInfo));
focus:=GetFocus;
dc:=GetDC(focus);
try
errCode:=GetDIBits(dc, bitmap, 0, TBitmapInfoHeader(bitmapInfo).biHeight,
@bits, TBitmapInfo(bitmapInfo), DIB_RGB_COLORS);
Result:=(errCode<>0);
finally
ReleaseDC(focus, dc);
end;
end;
// ------------------
// ------------------ TAVIRecorder ------------------
// ------------------
// Create
//
constructor TAVIRecorder.Create(AOwner : TComponent);
begin
inherited;
FWidth:=320; // default values
FHeight:=200;
FFPS:=25;
FCompressor:=acDefault;
RecorderState:=rsNone;
FSizeRestriction:=srForceBlock8x8;
FImageRetrievalMode:=irmBitBlt;
end;
// Destroy
//
destructor TAVIRecorder.Destroy;
begin
// if still open here, abort it
if RecorderState=rsRecording then
CloseAVIFile(True);
inherited;
end;
// Restricted
//
function TAVIRecorder.Restricted(s : Integer) : Integer;
begin
case FSizeRestriction of
srForceBlock2x2 : Result:=(s div 2)*2;
srForceBlock4x4 : Result:=(s div 4)*4;
srForceBlock8x8 : Result:=(s div 8)*8;
else
Result:=s;
end;
end;
// SetHeight
//
procedure TAVIRecorder.SetHeight(const val : Integer);
begin
if (RecorderState<>rsRecording) and (val<>FHeight) and (val>0) then
FHeight:=Restricted(val);
end;
// SetWidth
//
procedure TAVIRecorder.SetWidth(const val : Integer);
begin
if (RecorderState<>rsRecording) and (val<>FWidth) and (val>0) then
FWidth:=Restricted(val);
end;
// SetSizeRestriction
//
procedure TAVIRecorder.SetSizeRestriction(const val : TAVISizeRestriction);
begin
if val<>FSizeRestriction then begin
FSizeRestriction:=val;
FHeight:=Restricted(FHeight);
FWidth:=Restricted(FWidth);
end;
end;
InternalAddAVIFrame;
end;
// InternalAddAVIFrame
//
procedure TAVIRecorder.InternalAddAVIFrame;
begin
if Assigned(FOnPostProcessEvent) then
FOnPostProcessEvent(Self, AVIBitmap);
with AVIBitmap do begin
InternalGetDIB(Handle, FBitmapInfo^, FBitmapBits^);
if AVIStreamWrite(Stream_c, AVIFrameIndex, 1, FBitmapBits, FBitmapSize,
AVIIF_KEYFRAME, nil, nil)<>AVIERR_OK then
raise Exception.Create('Add Frame Error');
Inc(AVIFrameIndex);
end;
end;
Result:=(FTempName<>'');
if Result then begin
if FileExists(FTempName) then begin
Result:=(MessageDlg(Format('Overwrite file %s?', [FTempName]),
mtConfirmation, [mbYes, mbNo], 0)=mrYes);
// AVI streamers don't trim the file they're written to, so start from
zero
if Result then
DeleteFile(FTempName);
end;
end;
AVIBitmap:=TBitmap.Create;
AVIFrameIndex:=0;
RecorderState:=rsRecording;
try
AVIBitmap.PixelFormat := pf24Bit;
AVIBitmap.Width := FWidth;
AVIBitmap.Height := FHeight;
// note: a filename with extension other then AVI give generate an error.
if AVIFileOpen(pfile, PChar(FTempName), OF_WRITE or OF_CREATE,
nil)<>AVIERR_OK then
raise Exception.Create('Cannot create AVI file. Disk full or file in
use?');
FillChar(asi,sizeof(asi),0);
with AVIBitmap do
InternalGetDIB( Handle, FBitmapInfo^, FBitmapBits^);
galpAVIOptions:=@gaAVIOptions;
fillchar(gaAVIOptions, SizeOf(gaAVIOptions), 0);
gaAVIOptions.fccType:=streamtypeVIDEO;
case FCompressor of
acShowDialog : begin
// call a dialog box for the user to choose the compressor options
AVISaveOptions(0, ICMF_CHOOSE_KEYFRAME or ICMF_CHOOSE_DATARATE, 1,
Stream, galpAVIOptions);
end;
acDivX : with gaAVIOptions do begin
// ask for generic divx, using current default settings
fccHandler:=mmioFOURCC('d','i','v','x');
end;
else
with gaAVIOptions do begin // or, you may want to fill the compression
options yourself
fccHandler:=mmioFOURCC('M','S','V','C'); // User MS video 1 as default.
// I guess it is installed on
every Win95 or later.
dwQuality:=7500; // compress quality 0-10,000
dwFlags:=0; // setting dwFlags to 0 would lead to some default
settings
end;
end;
AVI_DPI:=DPI;
except
CloseAVIFile(true);
raise;
end;
end;
AVIBitmap.Free;
FreeMem(FBitmapInfo);
FreeMem(FBitmapBits);
if UserAbort then
DeleteFile(FTempName);
finally
RecorderState:=rsNone;
end;
end;
// Recording
//
function TAVIRecorder.Recording : Boolean;
begin
Result:=(RecorderState=rsRecording);
end;
end.