BSOne.SFC/eCrmHE/EXE_bs1rcd/DBs1RcdMain.pas

1135 lines
30 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

unit DBs1RcdMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
FFBaseComponent, FFEncode, Vcl.WinXCtrls, Vcl.StdCtrls, DFindWindow,
Vcl.Buttons, System.Actions, Vcl.ActnList, ManagerConfig,
Vcl.Menus, System.ImageList, Vcl.ImgList, PngImageList, FFJoin,
DWaitWork, Tocsg.Trace, ThdRecordWait;
type
TDlgBs1RcdMain = class(TForm)
pnTop: TPanel;
btnSetFile: TSpeedButton;
btnRecPause: TSpeedButton;
btnRecStop: TSpeedButton;
SaveDialog: TSaveDialog;
FFEncoder: TFFEncoder;
btnShowFrame: TSpeedButton;
btnShowFrameDummy: TSpeedButton;
pnClient: TPanel;
pnBottom: TPanel;
lbPos: TLabel;
lbSize: TLabel;
btnFindWindow: TBitBtn;
ActionList: TActionList;
acCancelFindWnd: TAction;
popTray: TPopupMenu;
btnConfig: TSpeedButton;
miSetFile: TMenuItem;
miRecPause: TMenuItem;
miRecStop: TMenuItem;
N5: TMenuItem;
miExit: TMenuItem;
N7: TMenuItem;
btnStayOnTop: TSpeedButton;
btnStayOnTopDummy: TSpeedButton;
tReTry: TTimer;
FFJoiner: TFFJoiner;
tClearFile: TTimer;
btnTest: TButton;
tInit: TTimer;
procedure btnSetFileClick(Sender: TObject);
procedure btnRecPauseClick(Sender: TObject);
procedure btnRecStopClick(Sender: TObject);
procedure btnShowFrameClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure btnFindWindowMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure btnFindWindowMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure btnFindWindowMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure acCancelFindWndExecute(Sender: TObject);
procedure btnConfigClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure miExitClick(Sender: TObject);
procedure btnStayOnTopClick(Sender: TObject);
procedure FFEncoderTerminate(Sender: TObject;
const ATerminateInfo: TTerminateInfo);
procedure tReTryTimer(Sender: TObject);
procedure tClearFileTimer(Sender: TObject);
procedure btnTestClick(Sender: TObject);
procedure tInitTimer(Sender: TObject);
private
{ Private declarations }
sOutMovPath_: String;
nPauseFlag_: Integer;
rtRecFrame_: TRect;
DlgFindWnd_: TDlgFindWindow;
bFindingWnd_: Boolean;
hPreTopHWND_: HWND;
Trace_: TTgTrace;
Config_: TManagerConfig;
RecPathList_: TStringList;
dlgWait_: TDlgWaitWork;
nLeft_, nTop_,
nWidth_, nHeight_: Integer;
llMergeSize_: LONGLONG;
ThdRecordWait_: TThdRecordWait;
function StartRecord: Boolean;
procedure StopRecord;
procedure ShowWindow(bVal: Boolean);
procedure ExpandWindow(bVal: Boolean);
procedure UpdateRecFramePositionInfo;
procedure UpdateRecFrameSizeInfo;
function MergeVideo: Boolean;
procedure StopMergeWait(bClearFiles: Boolean = false);
public
{ Public declarations }
Constructor Create(aOwner: TComponent); override;
Destructor Destroy; override;
procedure process_WM_WINDOWPOSCHANGING(var msg: TMessage); Message WM_WINDOWPOSCHANGING;
procedure process_WM_SYSCOMMAD( var msg: TWMSysCommand); Message WM_SYSCOMMAND;
procedure process_WM_START_RECORD(var msg: TMessage); Message WM_START_RECORD;
procedure process_WM_STOP_RECORD(var msg: TMessage); Message WM_STOP_RECORD;
procedure process_WM_COPYDATA(var msg: TMessage); Message WM_COPYDATA;
end;
var
DlgBs1RcdMain: TDlgBs1RcdMain;
implementation
uses
GDICapture, Winapi.DwmApi, DConfig, Define, AVCodecStubs, Tocsg.Path,
Tocsg.Exception, Tocsg.Files, Tocsg.Safe, Tocsg.WndUtil, superobject, DefineHelper;
resourcestring
RS_FailHotKey1 = 'HOKEY_REC_RESUMPAUSE <20><>Ű <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>';
RS_FailHotKey2 = 'HOKEY_REC_STOP <20><>Ű <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>';
RS_NotFoundLibAv = '<27><><EFBFBD>ڴ<EFBFBD> <20><><EFBFBD>̺귯<CCBA><EAB7AF><EFBFBD><EFBFBD> ã<><C3A3> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>.';
RS_ErrorRecoding = '<27><>ȭ<EFBFBD><C8AD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>߻<EFBFBD><DFBB>߽<EFBFBD><DFBD>ϴ<EFBFBD>...';
RS_TerminatedRecord = '<27><>ȭ<EFBFBD><C8AD> <20>ߴ<EFBFBD> <20>Ǿ<EFBFBD><C7BE><EFBFBD><EFBFBD>ϴ<EFBFBD>.';
RS_NotFoundRecFrame = '<27><>ȭ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ã<><C3A3> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>.';
RS_ErrorInitWork = '<27>۾<EFBFBD><DBBE><EFBFBD> <20>غ<EFBFBD><D8BA>ϴ<EFBFBD><CFB4><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>߻<EFBFBD><DFBB>߽<EFBFBD><DFBD>ϴ<EFBFBD>.';
RS_Stop = '<27><><EFBFBD><EFBFBD>';
RS_ScreenRecStop = 'ȭ<><C8AD> <20><>ȭ <20><><EFBFBD><EFBFBD>';
RS_Restart = '<27><><EFBFBD><EFBFBD>';
RS_ScreenRecRestart = 'ȭ<><C8AD> <20><>ȭ <20><><EFBFBD><EFBFBD>';
RS_Pause = '<27><><EFBFBD><EFBFBD>';
RS_ScreenRecPause = 'ȭ<><C8AD> <20><>ȭ <20><><EFBFBD><EFBFBD>';
RS_Rec = '<27><>ȭ';
RS_PlzRecStop = 'ȭ<><C8AD> <20><>ȭ<EFBFBD><C8AD><EFBFBD>Դϴ<D4B4>. <20><><EFBFBD><EFBFBD> <20><>ȭ<EFBFBD><C8AD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֽʽÿ<CABD>.';
{$R *.dfm}
const
FRAME_MARGIN = 5;
HOKEY_REC_RESUMPAUSE = 1;
HOKEY_REC_STOP = 2;
{ TDlgQtRecMain }
Constructor TDlgBs1RcdMain.Create(aOwner: TComponent);
procedure InitCtrls;
var
sLogPath: String;
i: Integer;
begin
Caption := APP_NAME;
{$IFDEF DEBUG}
sLogPath := CutFileExt(GetRunExePath) + '.log';
if FileExists(sLogPath) then
DeleteFile(sLogPath);
Trace_ := TTgTrace.Create(sLogPath);
{$ELSE}
{$ENDIF}
for i := 0 to pnTop.ControlCount - 1 do
begin
if pnTop.Controls[i] is TSpeedButton then
TSpeedButton(pnTop.Controls[i]).Flat := true;
end;
ExpandWindow(Config_.ShowRecArea);
btnShowFrame.Down := Config_.ShowRecArea;
if Config_.SavePosSize then
begin
Position := poDesigned;
Left := Config_.Left;
Top := Config_.Top;
if (BorderStyle = bsSizeable) and (Config_.Width > 0) then
begin
Width := Config_.Width;
Height := Config_.Height;
end;
end;
pnClient.Left := FRAME_MARGIN;
pnClient.Top := pnTop.Height + FRAME_MARGIN;
pnClient.Width := ClientWidth - (FRAME_MARGIN * 2);
pnClient.Height := ClientHeight - pnTop.Height - pnBottom.Height - (FRAME_MARGIN * 2);
pnClient.Anchors := [akLeft, akTop, akRight, akBottom];
lbSize.Left := pnBottom.Width - lbSize.Width - 8;
UpdateRecFramePositionInfo;
UpdateRecFrameSizeInfo;
// Ctrl + Alt + Shift + P
// if not RegisterHotKey(Handle, HOKEY_REC_RESUMPAUSE, MOD_CONTROL or MOD_ALT or MOD_SHIFT, $50) then
// TTgTrace.T(RS_FailHotKey1);
// Ctrl + Alt + Shift + O
// if not RegisterHotKey(Handle, HOKEY_REC_STOP, MOD_CONTROL or MOD_ALT or MOD_SHIFT, $4F) then
// TTgTrace.T(RS_FailHotKey2);
if Config_.StayOnTop then
btnStayOnTop.Click;
end;
begin
Inherited Create(aOwner);
sOutMovPath_ := '';
bFindingWnd_ := false;
hPreTopHWND_ := 0;
nLeft_ := -1;
nTop_ := -1;
nWidth_ := -1;
nHeight_ := -1;
Config_ := TManagerConfig.Create;
DlgFindWnd_ := TDlgFindWindow.Create(Self);
RecPathList_ := TStringList.Create;
Trace_ := nil;
dlgWait_ := nil;
ThdRecordWait_ := nil;
ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD);
InitCtrls;
TTgTrace.T('Create() ..');
tInit.Enabled := true;
end;
Destructor TDlgBs1RcdMain.Destroy;
begin
TTgTrace.T('Destroy() ..');
UnregisterHotKey(Handle, HOKEY_REC_STOP);
UnregisterHotKey(Handle, HOKEY_REC_RESUMPAUSE);
FreeAndNil(Config_);
if Trace_ <> nil then
FreeAndNil(Trace_);
FreeAndNil(RecPathList_);
if dlgWait_ <> nil then
FreeAndNil(dlgWait_);
if ThdRecordWait_ <> nil then
FreeAndNil(ThdRecordWait_);
Inherited;
end;
procedure TDlgBs1RcdMain.UpdateRecFramePositionInfo;
var
pt: TPoint;
begin
pt := pnClient.ClientToScreen(TPoint.Create(0, 0));
lbPos.Caption := Format('X : %d Y : %d', [pt.X, pt.Y]);
end;
procedure TDlgBs1RcdMain.UpdateRecFrameSizeInfo;
begin
lbSize.Caption := Format('%d x %d', [pnClient.Width, pnClient.Height]);
end;
procedure TDlgBs1RcdMain.btnSetFileClick(Sender: TObject);
begin
if not FFEncoder.AVLibLoaded then
begin
if not FFEncoder.LoadAVLib(GetRunExePathDir + DIR_AVLIB) then
begin
MessageBox(Handle, PChar(RS_NotFoundLibAv),
PChar(Caption), MB_ICONSTOP or MB_OK);
exit;
end;
// register gdi(screen/wave) capture demuxer
GDICapture.register_gdicapture;
// required for FFJoiner
FFJoin.register_ffjoin;
end;
SaveDialog.Filter := 'MPEG-4|*.mp4';
SaveDialog.FileName := '';
if SaveDialog.Execute(Handle) then
begin
nLeft_ := -1;
nTop_ := -1;
nWidth_ := -1;
nHeight_ := -1;
RecPathList_.Clear;
FFEncoder.ClearTasks;
sOutMovPath_ := SaveDialog.FileName;
if GetFileExt(sOutMovPath_).ToUpper <> 'MP4' then
sOutMovPath_ := sOutMovPath_ + '.mp4';
btnSetFile.Enabled := false;
miSetFile.Enabled := false;
btnRecPause.Enabled := true;
miRecPause.Enabled := true;
Caption := sOutMovPath_;
end;
end;
procedure TDlgBs1RcdMain.ExpandWindow(bVal: Boolean);
begin
if bVal then
begin
BorderStyle := bsSizeable;
ClientHeight := 360;
Constraints.MinHeight := 120;
pnBottom.Visible := true;
btnFindWindow.Enabled := true;
pnClient.Height := ClientHeight - pnTop.Height - pnBottom.Height - (FRAME_MARGIN * 2);
lbSize.Left := pnBottom.Width - lbSize.Width - 8;
UpdateRecFramePositionInfo;
UpdateRecFrameSizeInfo;
end else begin
Constraints.MinHeight := 0;
Width := 410;
BorderStyle := bsSingle;
ClientHeight := pnTop.Height;
pnBottom.Visible := false;
btnFindWindow.Enabled := false;
end;
end;
procedure TDlgBs1RcdMain.FFEncoderTerminate(Sender: TObject;
const ATerminateInfo: TTerminateInfo);
var
dlgWait: TDlgWaitWork;
begin
if ATerminateInfo.Exception then
begin
btnRecStop.Click;
TTgTrace.T('Fail .. %s', [ATerminateInfo.ExceptionMsg]);
MessageBox(Handle, PChar(Format(RS_ErrorRecoding+#13+#10+'(%s)', [ATerminateInfo.ExceptionMsg])),
PChar(Caption), MB_ICONWARNING or MB_OK);
end else
if ATerminateInfo.Finished and (btnRecPause.Tag <> 0) then
begin
if Config_.ReTryRec then
begin
tReTry.Enabled := true;
end else begin
btnRecStop.Click;
MessageBox(Handle, PChar(RS_TerminatedRecord), PChar(Caption), MB_ICONINFORMATION or MB_OK);
end;
end;
StopMergeWait(true);
end;
procedure TDlgBs1RcdMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FFEncoder.Stop;
FFEncoder.ClearTasks;
if Config_.SavePosSize then
begin
Config_.Left := Left;
Config_.Top := Top;
if BorderStyle = bsSizeable then
begin
Config_.Width := Width;
Config_.Height := Height;
end;
Config_.Save;
end;
end;
procedure TDlgBs1RcdMain.FormResize(Sender: TObject);
begin
// UpdateRecFramePositionInfo;
// UpdateRecFrameSizeInfo;
end;
procedure TDlgBs1RcdMain.miExitClick(Sender: TObject);
begin
if btnRecPause.Tag <> 0 then
begin
MessageBox(Handle, PChar(RS_PlzRecStop), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
Close;
end;
procedure TDlgBs1RcdMain.btnShowFrameClick(Sender: TObject);
begin
if BorderStyle = bsSizeable then
begin
ExpandWindow(false);
btnShowFrameDummy.Down := true;
end else begin
ExpandWindow(true);
btnShowFrame.Down := true;
end;
Config_.ShowRecArea := btnShowFrame.Down;
Config_.Save;
end;
procedure TDlgBs1RcdMain.btnStayOnTopClick(Sender: TObject);
begin
if FormStyle = fsNormal then
begin
FormStyle := fsStayOnTop;
Config_.StayOnTop := true;
btnStayOnTop.Down := true;
end else begin
FormStyle := fsNormal;
Config_.StayOnTop := false;
btnStayOnTopDummy.Down := true;
end;
Config_.Save;
end;
procedure TDlgBs1RcdMain.btnTestClick(Sender: TObject);
begin
RecPathList_.Clear;
RecPathList_.Add('C:\Users\kku\Desktop\22-00.mp4');
RecPathList_.Add('C:\Users\kku\Desktop\22-01.mp4');
RecPathList_.Add('C:\Users\kku\Desktop\22-02.mp4');
RecPathList_.Add('C:\Users\kku\Desktop\22-03.mp4');
nWidth_ := 1000;
nHeight_ := 800;
MergeVideo;
end;
function CalcScaledSize(const SrcW, SrcH: Integer;
const MaxW, MaxH: Integer;
out DstW, DstH: Integer): Boolean;
var
ScaleW, ScaleH, Scale: Double;
begin
Result := false;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>̹<EFBFBD> <20><><EFBFBD>Ѻ<EFBFBD><D1BA><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>״<EFBFBD><D7B4><EFBFBD> <20><><EFBFBD><EFBFBD>
if (SrcW <= MaxW) and (SrcH <= MaxH) then
begin
DstW := SrcW;
DstH := SrcH;
exit;
end;
// <20><> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
ScaleW := MaxW / SrcW;
ScaleH := MaxH / SrcH;
// <20><> <20><><EFBFBD><EFBFBD> <20>ٿ<EFBFBD><D9BF><EFBFBD> <20>ϴ<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ؾ<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
if ScaleW < ScaleH then
Scale := ScaleW
else
Scale := ScaleH;
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>ػ<EFBFBD><D8BB><EFBFBD> <20><><EFBFBD><EFBFBD>
DstW := Round(SrcW * Scale);
DstH := Round(SrcH * Scale);
// ¦<><C2A6> <20><><EFBFBD><EFBFBD>(<28><><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD> mod2 <20><EFBFBD>ϴ<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>)
if (DstW mod 2) <> 0 then
Dec(DstW);
if (DstH mod 2) <> 0 then
Dec(DstH);
Result := true;
end;
function TDlgBs1RcdMain.StartRecord: Boolean;
var
IO: TInputOptions;
OO: TOutputOptions;
nTaskIdx, nFps: Integer;
pt: TPoint;
sMsg,
sOutPath: String;
nW, nH: Integer;
bProcScale: Boolean;
begin
Result := false;
try
nPauseFlag_ := 0;
// <20>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD> <20>ٽ<EFBFBD> <20><>ȭ<EFBFBD>ϴ°<CFB4><C2B0><EFBFBD> Ȯ<><C8AE> 21_1118 08:05:58 sunk
if (nWidth_ = -1) or (nHeight_ = -1) then
begin
if BorderStyle = bsSizeable then
begin
pt := pnClient.ClientToScreen(TPoint.Create(0, 0));
nLeft_ := pt.X;
nTop_ := pt.Y;
nWidth_ := pnClient.Width;
nHeight_ := pnClient.Height;
end else begin
// nLeft_ := 0;//GetSystemMetrics(SM_XVIRTUALSCREEN);
// nTop_ := 0;//GetSystemMetrics(SM_YVIRTUALSCREEN);
// nWidth_ := 3840;//GetSystemMetrics(SM_CXVIRTUALSCREEN);
// nHeight_ := 2160;//GetSystemMetrics(SM_CYVIRTUALSCREEN);
nLeft_ := GetSystemMetrics(SM_XVIRTUALSCREEN);
nTop_ := GetSystemMetrics(SM_YVIRTUALSCREEN);
nWidth_ := GetSystemMetrics(SM_CXVIRTUALSCREEN);
nHeight_ := GetSystemMetrics(SM_CYVIRTUALSCREEN);
end;
if (nWidth_ = 0) or (nHeight_ = 0) then
begin
nWidth_ := -1;
nHeight_ := -1;
MessageBox(Handle, PChar(RS_NotFoundRecFrame), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
// ũ<><C5A9> <20><><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ũ<>Ⱑ ¦<><C2A6><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ȴ<EFBFBD> 21_0721 08:15:50 sunk
if (nWidth_ mod 2) <> 0 then
Dec(nWidth_);
if (nHeight_ mod 2) <> 0 then
Dec(nHeight_);
end;
case Config_.FrameRate of
1 : nFps := 25;
2 : nFps := 30;
else nFps := 15;
end;
bProcScale := CalcScaledSize(nWidth_, nHeight_, 1920, 1200, nW, nH);
InitInputOptions(@IO);
IO.Options := '-f gdicapture' + // gdi(screen/wave) capture format
' -pause_pointer ' + IntToStr(NativeUInt(@nPauseFlag_)) + // integer value of pause flag pointer
' -capture_offset ' + Format('%d,%d', [nLeft_, nTop_]) + // capture offset %d,%d: offset on x and y against the final source window
// capture flags: +/-showframe: show flashing frame, +/-client: capture client dc instead of window dc, +/-cursor: capture cursor, +/-checkbound: check input bound
' -s ' + Format('%dx%d', [nWidth_, nHeight_]) +
Format(' -r %d', [nFps]) + ' -capture_flags +client+cursor';
if (BorderStyle <> bsSizeable) or Config_.NoShowRecFrame then
IO.Options := IO.Options + '-showframe';
TTgTrace.T('Encoder Init .. Input Options="%s"', [IO.Options]);
// try to open input file
// filename format: video=hwnd:audio=device_id
// hwnd for screen capture: special window handle; 0 indicate desktop
// device_id for wave capture: device identifier, such as -1 for WAVE_MAPPER
nTaskIdx := FFEncoder.AddTask('video=0', @IO);
if nTaskIdx < 0 then
begin
TTgTrace.T('Encoder error .. "%s"', [FFEncoder.LastErrMsg]);
exit;
end;
TTgTrace.T('Encoder Init .. "%s"', [FFEncoder.Decoders[nTaskIdx, 0].FileInfoText]);
// set output options
InitOutputOptions(@OO);
// mp4 output options
// OO.Options := '-c:v libx264 -pix_fmt yuv420p -g 15 -preset ultrafast -b:v 500';
// OO.Options := '-vf scale=1680:1050 -c:v libx264 -pix_fmt yuv420p -g 15 ';
if bProcScale then
OO.Options := Format('-vf scale=%d:%d -c:v libx264 -pix_fmt yuv420p -g 15 ', [nW, nH])
else
OO.Options := '-c:v libx264 -pix_fmt yuv420p -g 15 ';
case Config_.RecQuality of
0 : OO.Options := OO.Options + '-b:v 2000k -qscale 255';
2 : OO.Options := OO.Options + '-preset ultrafast -b:v 500k';
else OO.Options := OO.Options + '-b:v 1000k';
end;
TTgTrace.T('Encoder Init .. Output Options="%s"', [OO.Options]);
// try to set output file with output options
if RecPathList_.Count > 0 then
begin
if RecPathList_.Count = 1 then
begin
// ù<><C3B9>° <20><><EFBFBD><EFBFBD> <20≯<EFBFBD><CCB8><EFBFBD><EFBFBD><EFBFBD>
sOutPath := Format('%s-00%s', [CutFileExt(sOutMovPath_), ExtractFileExt(sOutMovPath_)]);
if not RenameFile(sOutMovPath_, sOutPath) then
begin
TTgTrace.T('StartRecord() : Fail .. Rename .. OldPath="%s", NewPath="%s"', [sOutMovPath_, sOutPath]);
exit;
end;
RecPathList_[0] := sOutPath;
end;
sOutPath := Format('%s-%.2d%s', [CutFileExt(sOutMovPath_), RecPathList_.Count, ExtractFileExt(sOutMovPath_)]);
end else
sOutPath := sOutMovPath_;
if FFEncoder.SetOutput(nTaskIdx, sOutPath, @OO) < 0 then
begin
FFEncoder.RemoveTask(nTaskIdx);
sMsg := Format('Encoder error .. Cannot do convert, "%s"', [FFEncoder.LastErrMsg]);
TTgTrace.T(sMsg);
MessageBox(Handle, PChar(sMsg), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
if Config_.StartRecToTray then
ShowWindow(false);
TTgTrace.T('Encoder Can do convert ..');
FFEncoder.Start(1);
if RecPathList_.IndexOf(sOutPath) = -1 then
RecPathList_.Add(sOutPath);
Result := true;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. StartRecord()');
end;
end;
procedure TDlgBs1RcdMain.StopRecord;
begin
btnRecPause.Tag := 0;
btnRecPause.Caption := RS_Rec;
btnRecPause.ImageIndex := 1;
btnRecPause.Enabled := false;
miRecPause.Enabled := false;
miRecPause.ImageIndex := 1;
btnRecStop.Enabled := false;
miRecStop.Enabled := false;
btnSetFile.Enabled := true;
miSetFile.Enabled := true;
btnShowFrame.Enabled := true;
Caption := APP_NAME;
if Config_.StartRecToTray then
ShowWindow(true);
FFEncoder.Stop;
FFEncoder.ClearTasks;
if RecPathList_.Count > 1 then
begin
// <20><><EFBFBD><EFBFBD> <20><>ġ<EFBFBD><C4A1> 21_1117 14:27:42 sunk
StopMergeWait;
Enabled := false;
dlgWait_ := TDlgWaitWork.Create(Self);
dlgWait_.Show;
Application.ProcessMessages;
Sleep(2000);
MergeVideo;
end;
// sOutMovPath_ := '';
end;
procedure TDlgBs1RcdMain.tClearFileTimer(Sender: TObject);
var
i: Integer;
begin
tClearFile.Enabled := false;
FFEncoder.Stop;
FFEncoder.ClearTasks;
FFJoiner.Reset;
if Config_.AutoDelMergeAv and
((llMergeSize_ div 2) < GetFileSize_path(sOutMovPath_)) then // <20><>ġ<EFBFBD><C4A1> <20><><EFBFBD>и<EFBFBD> <20>뷫 ũ<><C5A9><EFBFBD><EFBFBD> üũ...
begin
for i := 0 to RecPathList_.Count - 1 do
DeleteFile(RecPathList_[i]);
end;
RecPathList_.Clear;
end;
procedure TDlgBs1RcdMain.tInitTimer(Sender: TObject);
begin
tInit.Enabled := false;
case gParam.ExeType of
etTest : ;
etRecordWait :
begin
TTgTrace.T('tInitTimer() .. etRecordWait');
if not FFEncoder.AVLibLoaded then
begin
if not FFEncoder.LoadAVLib(GetRunExePathDir + DIR_AVLIB) then
begin
TTgTrace.T('Fail .. LoadAVLib()');
Close;
exit;
end;
// register gdi(screen/wave) capture demuxer
GDICapture.register_gdicapture;
// required for FFJoiner
FFJoin.register_ffjoin;
end;
ThdRecordWait_ := TThdRecordWait.Create(Handle, gParam.OwMtxName, gParam.TaskInfo);
ThdRecordWait_.StartThread;
end;
else Close;
end;
end;
procedure TDlgBs1RcdMain.tReTryTimer(Sender: TObject);
begin
tReTry.Enabled := false;
FFEncoder.Stop;
FFEncoder.ClearTasks;
// if BorderStyle = bsSizeable then
// ShowWindow(true);
btnRecPause.Tag := 0;
btnRecPause.Click;
end;
procedure TDlgBs1RcdMain.acCancelFindWndExecute(Sender: TObject);
begin
if bFindingWnd_ then
begin
hPreTopHWND_ := 0;
btnFindWindowMouseUp(nil, TMouseButton(0), [], 0, 0);
end;
end;
procedure TDlgBs1RcdMain.btnConfigClick(Sender: TObject);
var
dlg: TDlgConfig;
OldFormStyle: TFormStyle;
begin
Guard(dlg, TDlgConfig.Create(Self));
OldFormStyle := FormStyle;
if OldFormStyle = fsStayOnTop then
begin
// dlg.FormStyle := fsStayOnTop <20>̷<EFBFBD><CCB7><EFBFBD> <20><><EFBFBD>൵...
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> fsStayOnTop <20><> <20><><EFBFBD>¸<EFBFBD> <20><><EFBFBD><EFBFBD> <20>ڷ<EFBFBD> <20><><EFBFBD><20>̷<EFBFBD><CCB7><EFBFBD> ó<><C3B3><EFBFBD><EFBFBD> 21_0728 13:02:43 sunk
FormStyle := fsNormal;
dlg.FormStyle := fsStayOnTop;
end;
try
with Config_, dlg do
begin
chSavePosSize.Checked := SavePosSize;
chNoShowRecFrame.Checked := NoShowRecFrame;
chStartRecToTray.Checked := StartRecToTray;
chReTryRec.Checked := ReTryRec;
chAutoDelMergeAv.Checked := AutoDelMergeAv;
if RecQuality > -1 then
cbRecQuality.ItemIndex := RecQuality
else
cbRecQuality.ItemIndex := 1;
if FrameRate > -1 then
cbFrameRate.ItemIndex := FrameRate;
if dlg.ShowModal = mrOk then
begin
SavePosSize := chSavePosSize.Checked;
NoShowRecFrame := chNoShowRecFrame.Checked;
StartRecToTray := chStartRecToTray.Checked;
ReTryRec := chReTryRec.Checked;
AutoDelMergeAv := chAutoDelMergeAv.Checked;
RecQuality := cbRecQuality.ItemIndex;
FrameRate := cbFrameRate.ItemIndex;
Save;
end;
end;
finally
FormStyle := OldFormStyle;
end;
end;
procedure TDlgBs1RcdMain.btnFindWindowMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
bFindingWnd_ := true;
// btnFindWindow.Cursor := crCross;
SetCapture(btnFindWindow.Handle);
end;
procedure TDlgBs1RcdMain.btnFindWindowMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
var
pt: TPoint;
hTopHwnd,
hDeskHwnd,
hFindHwnd: HWND;
WndRect: TRect;
sClassName,
sProcName: String;
bGetWindRect: Boolean;
begin
if bFindingWnd_ then
begin
pt := btnFindWindow.ClientToScreen(TPoint.Create(X, Y));
hFindHwnd := WindowFromPoint(pt);
if hFindHwnd = 0 then
begin
hPreTopHWND_ := 0;
exit;
end;
hTopHwnd := GetTopParentHWND(hFindHwnd);
hDeskHwnd := FindWindowEx(0, 0, 'Progman', 'Program Manager');// <20><><EFBFBD><EFBFBD>ȭ<EFBFBD><C8AD> <20>ڵ<EFBFBD> <20><><EFBFBD><EFBFBD> // GetDesktopWindow;
if (hFindHwnd <> 0) and
(hFindHwnd <> Handle) and
(hTopHwnd <> Handle) and // <20><> <20><><EFBFBD>α׷<CEB1> <20><><EFBFBD><EFBFBD>
(hTopHwnd <> hDeskHwnd) and
(hFindHwnd <> hDeskHwnd) and
(hPreTopHWND_ <> hFindHwnd) then
begin
hPreTopHWND_ := hFindHwnd;
DlgFindWnd_.Hide;
bGetWindRect := DwmGetWindowAttribute(hPreTopHWND_, DWMWA_EXTENDED_FRAME_BOUNDS, @WndRect, SizeOf(WndRect)) = S_OK;
if not bGetWindRect then
bGetWindRect := GetWindowRect(hPreTopHWND_, WndRect);
if bGetWindRect then
begin
DlgFindWnd_.ShowFindRect(WndRect);
// TTgTrace.T('x=%d, y=%d', [pt.X, pt.Y]);
// TTgTrace.T('WndRect.x=%d, WndRect.y=%d', [WndRect.Left, WndRect.Top]);
end;
// TTgTrace.T('%d', [hFindHwnd]);
end;
end;
end;
procedure TDlgBs1RcdMain.btnFindWindowMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
nLeftGap, nTopGap, nWidthGap, nHeightGap: Integer;
pt: TPoint;
begin
DlgFindWnd_.Hide;
ReleaseCapture;
bFindingWnd_ := false;
// btnFindWindow.Cursor := crDefault;
try
if (hPreTopHWND_ <> 0) and (hPreTopHWND_ <> Handle) then
begin
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>ȭ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
pt := pnClient.ClientToScreen(TPoint.Create(0, 0));
nLeftGap := pt.X - Left;
nTopGap := pt.Y - Top;
nWidthGap := Width - pnClient.Width;
nHeightGap := Height - pnClient.Height;
Left := DlgFindWnd_.Left - nLeftGap;
Top := DlgFindWnd_.Top - nTopGap;
Width := DlgFindWnd_.Width + nWidthGap;
Height := DlgFindWnd_.Height + nHeightGap;
// <20>̰<EFBFBD> <20>ȵ<EFBFBD><C8B5>󰡼<EFBFBD> <20><><EFBFBD><EFBFBD> <20>ٽ<EFBFBD> <20><>ġ <20><><EFBFBD><EFBFBD>
lbSize.Left := pnBottom.Width - lbSize.Width - 8;
UpdateRecFramePositionInfo;
UpdateRecFrameSizeInfo;
end;
finally
hPreTopHWND_ := 0;
end;
end;
procedure TDlgBs1RcdMain.btnRecPauseClick(Sender: TObject);
begin
case btnRecPause.Tag of
0 :
begin
if not StartRecord then
begin
if Config_.StartRecToTray then
ShowWindow(true);
MessageBox(Handle, PChar(RS_ErrorInitWork),
PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
btnRecStop.Enabled := true;
miRecStop.Enabled := true;
btnShowFrame.Enabled := false;
btnRecPause.Tag := 1;
btnRecPause.Caption := RS_Stop;
btnRecPause.ImageIndex := 2;
miRecPause.Caption := RS_ScreenRecStop;
miRecPause.ImageIndex := 2;
end;
1 :
begin
btnRecPause.Tag := 2;
FFEncoder.Pause;
nPauseFlag_ := 1;
btnRecPause.Caption := RS_Restart;
btnRecPause.ImageIndex := 3;
miRecPause.Caption := RS_ScreenRecRestart;
miRecPause.ImageIndex := 3;
end;
2 :
begin
btnRecPause.Tag := 1;
FFEncoder.Resume;
nPauseFlag_ := 0;
btnRecPause.Caption := RS_Pause;
btnRecPause.ImageIndex := 2;
miRecPause.Caption := RS_ScreenRecPause;
miRecPause.ImageIndex := 2;
end;
end;
Application.ProcessMessages;
end;
procedure TDlgBs1RcdMain.StopMergeWait(bClearFiles: Boolean = false);
begin
if dlgWait_ <> nil then
begin
Enabled := true;
FreeAndNil(dlgWait_);
if bClearFiles then
tClearFile.Enabled := true;
end;
end;
function TDlgBs1RcdMain.MergeVideo: Boolean;
function AAC_Encoder: AnsiString;
begin
if Assigned(avcodec_find_encoder_by_name('libfdk_aac')) then
Result := 'libfdk_aac'
else if Assigned(avcodec_find_encoder_by_name('libaacplus')) then
Result := 'libaacplus'
else if Assigned(avcodec_find_encoder_by_name('libfaac')) then
Result := 'libfaac'
else if Assigned(avcodec_find_encoder_by_name('libvo_aacenc')) then
Result := 'libvo_aacenc'
else
Result := 'aac -strict experimental';
end;
var
i, nTaskIdx: Integer;
pt: TPoint;
IO: TInputOptions;
OO: TOutputOptions;
sMsg: String;
begin
Result := false;
try
if RecPathList_.Count < 2 then
exit;
if (nWidth_ = -1) or (nHeight_ = -1) then
exit;
FFJoiner.Reset;
for i := 0 to RecPathList_.Count - 1 do
begin
if FFJoiner.Add(RecPathList_[i]) < 0 then
begin
TTgTrace.T('MergeVideo() : File open error .. %s .. Path="%s"',
[FFJoiner.LastErrMsg, RecPathList_[i]]);
continue;
end;
Inc(llMergeSize_, GetFileSize_path(RecPathList_[i]));
end;
if FFJoiner.FileCount = 0 then
exit;
// ũ<><C5A9> <20><><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ũ<>Ⱑ ¦<><C2A6><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ȴ<EFBFBD> 21_0721 08:15:50 sunk
if (nWidth_ mod 2) <> 0 then
Dec(nWidth_);
if (nHeight_ mod 2) <> 0 then
Dec(nHeight_);
FFJoiner.DesireVideoWidth := nWidth_;
FFJoiner.DesireVideoHeight := nHeight_;
InitInputOptions(@IO);
IO.Options := '-f ffjoin'; // force to use ffjoin format
nTaskIdx := FFEncoder.AddTask(IntToStr(NativeUInt(FFJoiner)), @IO);
if nTaskIdx < 0 then
begin
TTgTrace.T('Encoder error .. "%s"', [FFEncoder.LastErrMsg]);
exit;
end;
// set output options
InitOutputOptions(@OO);
// mp4 output options
OO.Options := '-c:a ' + AAC_Encoder + ' -b:a 128k -ar 44100 ' +
Format('-c:v mpeg4 -s %dx%d ', [nWidth_, nHeight_]);
case Config_.RecQuality of
0 : OO.Options := OO.Options + '-b:v 2000k';
2 : OO.Options := OO.Options + '-b:v 500k';
else OO.Options := OO.Options + '-b:v 1000k';
end;
if FFEncoder.SetOutput(nTaskIdx, sOutMovPath_, @OO) < 0 then
begin
FFEncoder.RemoveTask(nTaskIdx);
sMsg := Format('Encoder error .. Cannot do convert, "%s"', [FFEncoder.LastErrMsg]);
TTgTrace.T(sMsg);
// MessageBox(Handle, PChar(sMsg), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
FFEncoder.Start(1);
Result := true;
finally
if not Result then
StopMergeWait;
end;
end;
procedure TDlgBs1RcdMain.btnRecStopClick(Sender: TObject);
begin
StopRecord;
end;
procedure TDlgBs1RcdMain.ShowWindow(bVal: Boolean);
begin
if bVal then
begin
var bOnTop: Boolean := FormStyle = fsStayOnTop;
if not bOnTop then
FormStyle := fsStayOnTop;
Show;
WindowState := wsNormal;
if not bOnTop then
FormStyle := fsNormal;
end else begin
WindowState := wsMinimized;
Hide;
end;
end;
procedure TDlgBs1RcdMain.process_WM_WINDOWPOSCHANGING(var msg: TMessage);
begin
if BorderStyle = bsSizeable then
begin
UpdateRecFramePositionInfo;
UpdateRecFrameSizeInfo;
end;
end;
procedure TDlgBs1RcdMain.process_WM_SYSCOMMAD( var msg: TWMSysCommand);
begin
if msg.CmdType = SC_CLOSE then
begin
miExit.Click;
exit;
end;
Inherited;
end;
procedure TDlgBs1RcdMain.process_WM_START_RECORD(var msg: TMessage);
var
O: ISuperObject;
begin
try
if ThdRecordWait_ = nil then
exit;
sOutMovPath_ := ThdRecordWait_.OutPath;
StartRecord;
O := SO;
O.S['RS'] := ThdRecordWait_.Reason;
if gParam.TaskInfo.hRcvWnd <> 0 then
SendData(gParam.TaskInfo.hRcvWnd, HPCMD_START_SCREEN_RECORD, O.AsJSon);
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. process_WM_START_RECORD()');
end;
end;
procedure TDlgBs1RcdMain.process_WM_STOP_RECORD(var msg: TMessage);
var
O: ISuperObject;
llMSec: LONGLONG;
begin
try
if ThdRecordWait_ = nil then
exit;
llMSec := GetTickCount64 - ThdRecordWait_.RcdTick;
StopRecord;
O := SO;
O.S['OD'] := sOutMovPath_;
O.S['RS'] := ThdRecordWait_.Reason;
O.I['MS'] := llMSec;
if gParam.TaskInfo.hRcvWnd <> 0 then
SendData(gParam.TaskInfo.hRcvWnd, HPCMD_STOP_SCREEN_RECORD, O.AsJSon);
sOutMovPath_ := '';
nLeft_ := -1;
nTop_ := -1;
nWidth_ := -1;
nHeight_ := -1;
RecPathList_.Clear;
FFEncoder.ClearTasks;
ThdRecordWait_.InitRecord;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. process_WM_STOP_RECORD()');
end;
end;
procedure TDlgBs1RcdMain.process_WM_COPYDATA(var msg: TMessage);
var
dwData: DWORD;
pCpData: PCopyDataStruct;
TaskInfo: TTaskInfo;
O: ISuperObject;
begin
msg.Result := 0;
dwData := 0;
pCpData := PCopyDataStruct(msg.LParam);
try
dwData := pCpData.dwData;
case dwData of
9091 : // <20><>å <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ
begin
if ThdRecordWait_ = nil then
exit;
O := SO(Copy(PChar(pCpData.lpData), 1, pCpData.cbData));
TaskInfo := ThdRecordWait_.TaskInfo;
TaskInfo.sApps := O.S['Apps'];
TaskInfo.nMaxMain := O.I['MaxMin'];
ThdRecordWait_.TaskInfo := TaskInfo;
end;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. process_WM_COPYDATA()');
end;
end;
end.