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 ÇÖŰ µî·Ï ½ÇÆÐ'; RS_FailHotKey2 = 'HOKEY_REC_STOP ÇÖŰ µî·Ï ½ÇÆÐ'; RS_NotFoundLibAv = 'ÀÎÄÚ´õ ¶óÀ̺귯¸®¸¦ ãÀ» ¼ö ¾ø½À´Ï´Ù.'; RS_ErrorRecoding = '³ìÈ­Áß ¿À·ù°¡ ¹ß»ýÇß½À´Ï´Ù...'; RS_TerminatedRecord = '³ìÈ­°¡ Áß´Ü µÇ¾ú½À´Ï´Ù.'; RS_NotFoundRecFrame = '³ìÈ­ ÇÁ·¹ÀÓÀ» ãÀ» ¼ö ¾ø½À´Ï´Ù.'; RS_ErrorInitWork = 'ÀÛ¾÷À» ÁغñÇÏ´ÂÁß ¿À·ù°¡ ¹ß»ýÇß½À´Ï´Ù.'; RS_Stop = 'Á¤Áö'; RS_ScreenRecStop = 'È­¸é ³ìÈ­ Á¤Áö'; RS_Restart = 'Àç°Ô'; RS_ScreenRecRestart = 'È­¸é ³ìÈ­ Àç°Ô'; RS_Pause = 'Á¤Áö'; RS_ScreenRecPause = 'È­¸é ³ìÈ­ Á¤Áö'; RS_Rec = '³ìÈ­'; RS_PlzRecStop = 'È­¸é ³ìÈ­ÁßÀÔ´Ï´Ù. ¸ÕÀú ³ìÈ­¸¦ ÁßÁöÇØÁֽʽÿÀ.'; {$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; // ¿øº»ÀÌ ÀÌ¹Ì Á¦ÇѺ¸´Ù ÀÛÀ¸¸é ±×´ë·Î »ç¿ë if (SrcW <= MaxW) and (SrcH <= MaxH) then begin DstW := SrcW; DstH := SrcH; exit; end; // °¢ Ãà ±âÁØ ½ºÄÉÀÏ °è»ê ScaleW := MaxW / SrcW; ScaleH := MaxH / SrcH; // ´õ ¸¹ÀÌ ÁÙ¿©¾ß ÇÏ´Â ÂÊÀ» ¼±ÅÃÇØ¾ß ºñÀ² ±úÁöÁö ¾ÊÀ½ if ScaleW < ScaleH then Scale := ScaleW else Scale := ScaleH; // ½ÇÁ¦ Ãà¼Ò ÇØ»óµµ °è»ê DstW := Round(SrcW * Scale); DstH := Round(SrcH * Scale); // ¦¼ö Á¶Á¤(ÀÎÄÚ´õ°¡ mod2 ¿ä±¸ÇÏ´Â °æ¿ì ´ëºñ) 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; // ÀÚµ¿À¸·Î ´Ù½Ã ³ìÈ­Çϴ°ÇÁö È®ÀÎ 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; // Å©±â Á¶Á¤, ¿µ»óÀº Å©±â°¡ ¦¼ö·Î ³¡³ª¾ß µÈ´Ù 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 // ù¹øÂ° ÆÄÀÏ À̸§º¯°æ 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 // ¿µ»ó ÇÕÄ¡±â 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 // ÇÕÄ¡±â ½ÇÆÐ¸¦ ´ë·« Å©±â·Î üũ... 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 ÀÌ·¸°Ô ÇØÁ൵... // ¸ÞÀÎÀÌ fsStayOnTop ÀÌ »óÅÂ¸é °è¼Ó µÚ·Î ¼û¾î¼­ ÀÌ·¸°Ô ó¸®ÇÔ 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');// ¹ÙÅÁÈ­¸é ÇÚµé Á¦¿Ü // GetDesktopWindow; if (hFindHwnd <> 0) and (hFindHwnd <> Handle) and (hTopHwnd <> Handle) and // º» ÇÁ·Î±×·¥ ¹«½Ã (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 // ¸ÞÀÎ À©µµ¿ì¿Í ³ìÈ­ ¿µ¿ªÀÇ °¸Â÷ÀÌ 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; // ÀÌ°Ô ¾Èµû¶ó°¡¼­ Á÷Á¢ ´Ù½Ã À§Ä¡ º¸Á¤ 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; // Å©±â Á¶Á¤, ¿µ»óÀº Å©±â°¡ ¦¼ö·Î ³¡³ª¾ß µÈ´Ù 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 : // Á¤Ã¥ ¾÷µ¥ÀÌÆ® 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.