unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, Menus, FFmpeg, FFmpegVCL, FFmpegLogger;
type
TfrmMain = class(TForm)
btnAdd: TButton;
btnRemove: TButton;
btnClear: TButton;
btnStart: TButton;
btnStop: TButton;
btnPause: TButton;
btnResume: TButton;
btnExit: TButton;
btnWebSite: TButton;
lvFiles: TListView;
mmoLog: TMemo;
grpOption: TGroupBox;
Label1: TLabel;
chkThreadMode: TCheckBox;
edtThreadCount: TEdit;
chkPreview: TCheckBox;
grpLogLevel: TRadioGroup;
grpPriority: TRadioGroup;
FFVCL: TFFmpegVCL;
OpenDialog1: TOpenDialog;
PopupMenu1: TPopupMenu;
mnuAdd: TMenuItem;
mnuRemove: TMenuItem;
mnuClear: TMenuItem;
N1: TMenuItem;
mnuPlay: TMenuItem;
mnuOpenFolder: TMenuItem;
FFLogger: TFFLogger;
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormDestroy(Sender: TObject);
procedure btnAddClick(Sender: TObject);
procedure btnRemoveClick(Sender: TObject);
procedure btnClearClick(Sender: TObject);
procedure btnStartClick(Sender: TObject);
procedure btnStopClick(Sender: TObject);
procedure btnPauseClick(Sender: TObject);
procedure btnResumeClick(Sender: TObject);
procedure btnExitClick(Sender: TObject);
procedure btnWebSiteClick(Sender: TObject);
procedure chkPreviewClick(Sender: TObject);
procedure PopupMenu1Popup(Sender: TObject);
procedure mnuPlayClick(Sender: TObject);
procedure mnuOpenFolderClick(Sender: TObject);
procedure FFVCLCustomHook(Sender: TObject; AHookInfo: PHookInfo);
procedure FFVCLProgress(Sender: TObject; AProgressInfo: PProgressInfo);
procedure FFVCLTerminate(Sender: TObject; const ATerminateInfo: TTerminateInfo);
procedure grpLogLevelClick(Sender: TObject);
procedure FFLoggerLog(Sender: TObject; AThreadID: Cardinal;
ALogLevel: TLogLevel; const ALogMsg: string);
private
FFolderList: TStringList;
FOutFileList: TStringList;
procedure DoAddFile(const AIO: TInputOptions; const AOO: TOutputOptions);
public
end;
var
frmMain: TfrmMain;
implementation
uses
XPMan,
ShellAPI,
OptionFrm,
MyUtils;
var
GfrmOption: TfrmOption = nil;
const
CLibAVPath = 'LibAV';
SAppTitle = 'Demo of FFVCL %s';
SCaption = 'Demo of FFVCL %s - Delphi FFmpeg VCL Components';
SWebSiteC = 'http://www.CCAVC.com';
SWebSiteE = 'http://www.DelphiFFmpeg.com';
DEMO_INFO =
'* The Demo Version FFVCL only works in Delphi IDE' +
#13#10 +
'* Thread Mode means creating new threads to do converting tasks, ' +
'it dose not using the main thread so you can Stop/Pause/Resume ' +
'converting without block.' +
#13#10 +
' * Thread Count means the quantity of converting jobs in the same time.' +
#13#10 +
' * WARNING: if you want to debug your code in IDE such as Breakpoints, ' +
'please do not check Thread Mode, or choose Thread Priority below "Normal", ' +
'because Delphi Debugger maybe fall into trouble in Thread Mode.' +
#13#10 +
'* Log Level means the log messages level' +
#13#10 +
'* Thread Priority only works in Thread Mode' +
#13#10 +
#13#10;
LICENSE_SEED = $D24E9E33;
LICENSE_KEY =
'39EA968465F26B6CDCA1E51EC8FAC6392100A838813BD0E0F5575E31AC38AEE4' +
'34E3AF85FDC4B84FBC8BC88078E83D482D8CD226F013CF85BA90F88765D91977' +
'A8C2E0E345B052AFC342FF244A8D7A95306623716DDBB1B512A1F44F32D6731C' +
'49308768679B36FC8F6AEF3207ED6CC8EA5EF8F4D2F2AA0F2DC5F13654B78322';
CDialogOptions = [ofHideReadOnly, ofFileMustExist, ofEnableSizing];
CPictureFiles = '*.BMP;*.GIF;*.JPEG;*.JPG;*.PNG;';
CAudioFiles = '*.MP3;*.AAC;*.WAV;*.WMA;*.CDA;*.FLAC;*.M4A;*.MID;*.MKA;' +
'*.MP2;*.MPA;*.MPC;*.APE;*.OFR;*.OGG;*.RA;*.WV;*.TTA;*.AC3;*.DTS;';
CVideoFiles = '*.AVI;*.AVM;*.ASF;*.WMV;*.AVS;*.FLV;*.MKV;*.MOV;*.3GP;' +
'*.MP4;*.MPG;*.MPEG;*.DAT;*.OGM;*.VOB;*.RM;*.RMVB;*.TS;*.TP;*.IFO;*.NSV;';
CDialogFilter =
'Video/Audio/Picture Files|' + CVideoFiles + CAudioFiles + CPictureFiles +
'|Video Files|' + CVideoFiles +
'|Audio Files|' + CAudioFiles +
'|Picture Files|' + CPictureFiles +
'|All Files|*.*';
SHookFrameNumber = 'FFVCL - Frame Number: %d';
SHookTimeStamp = 'FFVCL - Time Stamp: %d';
SHookTextImage = 'FFVCL';
SHookReverseVertical = 'Reverse Picture Vertically';
SHookReverseHorizontal = 'Reverse Picture Horizontally';
var
SWebSite: string = SWebSiteE;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Application.Title := Format(SAppTitle, [FFVCL.Version]);
Self.Caption := Format(SCaption, [FFVCL.Version]);
if SysUtils.SysLocale.PriLangID = LANG_CHINESE then
SWebSite := SWebSiteC
else
SWebSite := SWebSiteE;
btnWebSite.Caption := SWebSite;
mmoLog.Text := DEMO_INFO;
FFolderList := TStringList.Create;
FOutFileList := TStringList.Create;
OpenDialog1.Options := CDialogOptions;
OpenDialog1.Filter := CDialogFilter;
FFVCL.SetLicenseKey(LICENSE_KEY, LICENSE_SEED);
end;
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
with FFVCL do
begin
OnCustomHook := nil;
OnProgress := nil;
OnTerminate := nil;
Stop;
end;
FFLogger.OnLog := nil;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
FreeAndNil(FFolderList);
FreeAndNil(FOutFileList);
end;
procedure TfrmMain.chkPreviewClick(Sender: TObject);
begin
FFVCL.Preview := chkPreview.Checked;
end;
procedure TfrmMain.btnAddClick(Sender: TObject);
begin
if not FFVCL.AVLibLoaded then
begin
if not FFVCL.LoadAVLib(ExePath + CLibAVPath) then
begin
mmoLog.Lines.Add(FFVCL.LastErrMsg);
Exit;
end;
end;
if not OpenDialog1.Execute then
Exit;
if not FFVCL.AVProbe.LoadFile(OpenDialog1.FileName) then
begin
mmoLog.Lines.Add('');
mmoLog.Lines.Add('***File load error: ' + FFVCL.AVProbe.LastErrMsg);
mmoLog.Lines.Add('');
Exit;
end;
try
if not Assigned(GfrmOption) then
GfrmOption := TfrmOption.Create(Self);
GfrmOption.AVProbe := FFVCL.AVProbe;
if GfrmOption.ShowModal = mrOk then
DoAddFile(GfrmOption.InputOptions, GfrmOption.OutputOptions);
finally
FFVCL.AVProbe.CloseFile;
end;
end;
procedure TfrmMain.DoAddFile(const AIO: TInputOptions; const AOO: TOutputOptions);
var
LIndex: Integer;
begin
LIndex := FFVCL.AddTask(AIO.FileName, @AIO);
if LIndex < 0 then
begin
mmoLog.Lines.Add('');
mmoLog.Lines.Add('***File open error: ' + FFVCL.LastErrMsg);
mmoLog.Lines.Add('');
Exit;
end;
if not FFVCL.SetOutputFile(LIndex, AOO.FileName, @AOO) then
begin
FFVCL.RemoveTask(LIndex);
mmoLog.Lines.Add('');
mmoLog.Lines.Add('***Cannot do convert, error: ' + FFVCL.LastErrMsg);
mmoLog.Lines.Add('');
Exit;
end;
with lvFiles.Items.Add do
begin
Caption := ExtractFileName(AIO.FileName);
if FFVCL.AVProbe.FileStreamInfo.Duration > 0 then
SubItems.Add(IntToStr(FFVCL.AVProbe.FileStreamInfo.Duration div 1000000))
else
SubItems.Add('N/A');
SubItems.Add(IntToStr(FFVCL.AVProbe.FileSize));
SubItems.Add(ExtractFileName(AOO.FileName));
SubItems.Add('');
SubItems.Add('');
SubItems.Add('');
SubItems.Add('');
end;
mmoLog.Lines.Add('');
mmoLog.Lines.Add('***File has been added to convert list.');
mmoLog.Lines.Add('');
btnRemove.Enabled := True;
btnClear.Enabled := True;
btnStart.Enabled := True;
FFolderList.Add(ExtractFilePath(AOO.FileName));
FOutFileList.AddObject(AOO.FileName, nil);
end;
procedure TfrmMain.btnRemoveClick(Sender: TObject);
begin
if lvFiles.ItemIndex >= 0 then
begin
FFVCL.RemoveTask(lvFiles.ItemIndex);
FFolderList.Delete(lvFiles.ItemIndex);
FOutFileList.Delete(lvFiles.ItemIndex);
lvFiles.Items.Delete(lvFiles.ItemIndex);
btnRemove.Enabled := lvFiles.Items.Count > 0;
btnClear.Enabled := lvFiles.Items.Count > 0;
btnStart.Enabled := lvFiles.Items.Count > 0;
end;
end;
procedure TfrmMain.btnClearClick(Sender: TObject);
begin
FFVCL.ClearTasks;
FFolderList.Clear;
FOutFileList.Clear;
lvFiles.Items.Clear;
btnAdd.Enabled := True;
btnRemove.Enabled := False;
btnClear.Enabled := False;
btnStart.Enabled := False;
end;
procedure TfrmMain.btnStartClick(Sender: TObject);
begin
FFVCL.ThreadPriority := TThreadPriority(grpPriority.ItemIndex);
btnAdd.Enabled := False;
btnRemove.Enabled := False;
btnClear.Enabled := False;
btnStart.Enabled := False;
btnStop.Enabled := chkThreadMode.Checked;
btnPause.Enabled := chkThreadMode.Checked;
btnResume.Enabled := False;
if chkThreadMode.Checked then
FFVCL.Start(StrToIntDef(edtThreadCount.Text, 1))
else
FFVCL.Start(0);
end;
procedure TfrmMain.btnStopClick(Sender: TObject);
begin
btnStop.Enabled := False;
FFVCL.Stop;
end;
procedure TfrmMain.btnPauseClick(Sender: TObject);
begin
btnPause.Enabled := False;
btnResume.Enabled := True;
FFVCL.Pause;
end;
procedure TfrmMain.btnResumeClick(Sender: TObject);
begin
btnPause.Enabled := True;
btnResume.Enabled := False;
FFVCL.Resume;
end;
procedure TfrmMain.btnWebSiteClick(Sender: TObject);
begin
ShellExecute(Application.Handle, 'Open',
PChar(LowerCase(SWebSite)), '',
PChar(ExtractFilePath(Application.ExeName)), 1);
end;
procedure TfrmMain.btnExitClick(Sender: TObject);
begin
Close;
end;
procedure TfrmMain.PopupMenu1Popup(Sender: TObject);
var
I: Integer;
begin
mnuAdd.Enabled := btnAdd.Enabled;
mnuRemove.Enabled := btnRemove.Enabled;
mnuClear.Enabled := btnClear.Enabled;
mnuOpenFolder.Enabled := lvFiles.Items.Count > 0;
if (lvFiles.ItemIndex >= 0) and (lvFiles.ItemIndex < FFolderList.Count) then
I := lvFiles.ItemIndex
else
I := FOutFileList.Count - 1;
mnuPlay.Enabled := (I >= 0) and Assigned(FOutFileList.Objects[I]);
end;
procedure TfrmMain.mnuPlayClick(Sender: TObject);
var
I: Integer;
begin
if (lvFiles.ItemIndex >= 0) and (lvFiles.ItemIndex < FFolderList.Count) then
I := lvFiles.ItemIndex
else
I := FOutFileList.Count - 1;
if (I >= 0) and Assigned(FOutFileList.Objects[I]) and FileExists(FOutFileList.Strings[I]) then
ShellExecute(Application.Handle, 'Open',
PChar(FOutFileList.Strings[I]), '',
PChar(ExtractFilePath(FOutFileList.Strings[I])), SW_SHOWNORMAL);
end;
procedure TfrmMain.mnuOpenFolderClick(Sender: TObject);
var
LPath: string;
begin
if (lvFiles.ItemIndex >= 0) and (lvFiles.ItemIndex < FFolderList.Count) then
LPath := FFolderList.Strings[lvFiles.ItemIndex]
else if FFolderList.Count > 0 then
LPath := FFolderList.Strings[FFolderList.Count - 1]
else
LPath := '';
if (LPath <> '') and DirectoryExists(LPath) then
ShellExecute(Application.Handle, 'Open',
PChar(LPath), nil,
PChar(LPath), SW_SHOWDEFAULT);
end;
procedure TfrmMain.FFVCLCustomHook(Sender: TObject; AHookInfo: PHookInfo);
const
CBytes = 3;
var
H, W: Integer;
I, J, K: Integer;
P1, P2: PAnsiChar;
B: AnsiChar;
begin
if AHookInfo.FrameNumber = 1 then
begin
with AHookInfo.Bitmap.Canvas.Font do
begin
Color := clWhite;
Name := 'Tahoma';
Size := 12;
Style := [fsBold, fsUnderline];
end;
end;
if AHookInfo.FrameNumber < 100 then
AHookInfo.Bitmap.Canvas.TextOut(10, 10, Format(SHookFrameNumber, [AHookInfo.FrameNumber]))
else if AHookInfo.FrameNumber < 150 then
AHookInfo.Update := False
else if AHookInfo.FrameNumber < 250 then
AHookInfo.Bitmap.Canvas.TextOut(10, 10, Format(SHookTimeStamp, [AHookInfo.PTS]))
else if AHookInfo.FrameNumber < 350 then
begin
if AHookInfo.FrameNumber = 250 then
with AHookInfo.Bitmap.Canvas.Font do
begin
Color := clRed;
Size := 16;
end;
AHookInfo.Bitmap.Canvas.TextOut(10, 10, SHookTextImage);
AHookInfo.Bitmap.Canvas.Draw(AHookInfo.Bitmap.Width - Application.Icon.Width - 10,
AHookInfo.Bitmap.Height - Application.Icon.Height - 10,
Application.Icon);
end
else if AHookInfo.FrameNumber < 450 then
begin
if AHookInfo.FrameNumber = 350 then
with AHookInfo.Bitmap.Canvas.Font do
begin
Color := clWhite;
Style := [fsBold];
end;
AHookInfo.Bitmap.Canvas.TextOut(10, 10, SHookReverseVertical);
H := AHookInfo.Bitmap.Height;
W := AHookInfo.Bitmap.Width;
for I := 0 to H div 2 - 1 do
begin
P1 := PAnsiChar(AHookInfo.Bitmap.ScanLine[I]);
P2 := PAnsiChar(AHookInfo.Bitmap.ScanLine[H - 1 - I]);
for J := 0 to CBytes * W - 1 do
begin
B := P1^;
P1^ := P2^;
P2^ := B;
Inc(P1);
Inc(P2);
end;
end;
end
else if AHookInfo.FrameNumber < 550 then
begin
if AHookInfo.FrameNumber = 350 then
with AHookInfo.Bitmap.Canvas.Font do
begin
Style := [];
end;
AHookInfo.Bitmap.Canvas.TextOut(10, 10, SHookReverseHorizontal);
H := AHookInfo.Bitmap.Height;
W := AHookInfo.Bitmap.Width;
for I := 0 to H - 1 do
begin
P1 := PAnsiChar(AHookInfo.Bitmap.ScanLine[I]);
P2 := P1 + CBytes * (W - 1);
for J := 0 to W div 2 - 1 do
begin
for K := 0 to CBytes - 1 do
begin
B := (P1 + K)^;
(P1 + K)^ := (P2 + K)^;
(P2 + K)^ := B;
end;
Inc(P1, CBytes);
Dec(P2, CBytes);
end;
end;
end
else
AHookInfo.StopHook := True;
end;
procedure TfrmMain.FFVCLProgress(Sender: TObject; AProgressInfo: PProgressInfo);
begin
with lvFiles.Items.Item[AProgressInfo.TaskIndex].SubItems, AProgressInfo^ do
begin
if TotalDuration > 0 then
Strings[3] := IntToStr(CurrentDuration * 100 div TotalDuration) + '%'
else
Strings[3] := 'N/A';
if FPS > 0 then
Strings[4] := IntToStr(FPS);
Strings[5] := IntToStr(CurrentDuration div 1000000);
Strings[6] := IntToStr(CurrentSize);
end;
if not chkThreadMode.Checked then
lvFiles.Repaint;
end;
procedure TfrmMain.FFVCLTerminate(Sender: TObject; const ATerminateInfo: TTerminateInfo);
begin
if ATerminateInfo.TaskIndex < 0 then
begin
btnAdd.Enabled := False;
btnRemove.Enabled := False;
btnClear.Enabled := True;
btnStart.Enabled := False;
btnStop.Enabled := False;
btnPause.Enabled := False;
btnResume.Enabled := False;
end
else if ATerminateInfo.Finished then
begin
lvFiles.Items.Item[ATerminateInfo.TaskIndex].SubItems.Strings[3] := '100%';
if not chkThreadMode.Checked then
begin
lvFiles.Repaint;
end;
end;
if (ATerminateInfo.TaskIndex >= 0) and not ATerminateInfo.Exception then
FOutFileList.Objects[ATerminateInfo.TaskIndex] := TObject(1);
if ATerminateInfo.Exception then
Application.MessageBox(PChar(ATerminateInfo.ExceptionMsg), PChar(Application.Title), MB_OK + MB_ICONWARNING);
end;
procedure TfrmMain.grpLogLevelClick(Sender: TObject);
begin
FFLogger.LogLevel := TLogLevel(grpLogLevel.ItemIndex);
end;
procedure TfrmMain.FFLoggerLog(Sender: TObject; AThreadID: Cardinal;
ALogLevel: TLogLevel; const ALogMsg: string);
begin
mmoLog.Lines.Add('#' + IntToStr(AThreadID) + '.' + IntToStr(Ord(ALogLevel)) + ': ' + ALogMsg);
end;
end.