Source of Demo Option Form

unit OptionFrm;

interface

uses
  Windows, Messages, SysUtils, {Variants,} Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls, FFmpegVCL, AVProbe, libavcodec,
  libavformat;

type
  TfrmOption = class(TForm)
    grpNorm: TRadioGroup;
    grpAudioChannels: TRadioGroup;
    edtStartTime: TEdit;
    edtEndTime: TEdit;
    edtAudioVolume: TEdit;
    grpFormats: TRadioGroup;
    grpAudioSampleRate: TRadioGroup;
    grpAspectRatio: TRadioGroup;
    grpAudioVolume: TGroupBox;
    chkAudioVolume: TCheckBox;
    grpCutClip: TGroupBox;
    chkCutClip: TCheckBox;
    btnOk: TButton;
    btnCancel: TButton;
    chkOverwrite: TCheckBox;
    cboFrameSize: TComboBox;
    grpFrameSize: TGroupBox;
    grpVideoCodec: TRadioGroup;
    grpAudioCodec: TRadioGroup;
    chkCopyTimestamp: TCheckBox;
    grpBitRate: TGroupBox;
    edtVideoBitRate: TEdit;
    edtAudioBitRate: TEdit;
    grpFrameRate: TRadioGroup;
    grpVideoHook: TGroupBox;
    chkVideoBitRate: TCheckBox;
    chkAudioBitRate: TCheckBox;
    rdoExternalHook: TRadioButton;
    pnlHook: TPanel;
    chkWaterMark: TCheckBox;
    chkImlib2Img: TCheckBox;
    chkImgDancing: TCheckBox;
    chkImlib2Txt: TCheckBox;
    chkTxtScrolling: TCheckBox;
    chkWaterMarkMode: TCheckBox;
    edtPTSs: TEdit;
    btnPTSs: TButton;
    rdoCustomHook: TRadioButton;
    rdoStandardHook: TRadioButton;
    rdoBeforeHook: TRadioButton;
    rdoPTSs: TRadioButton;
    grpAVFileInfo: TGroupBox;
    mmoAVFileInfo: TMemo;
    edtImlib2Txt: TEdit;
    chkDelphiHookImg: TCheckBox;
    chkDelphiHookTxt: TCheckBox;
    edtDelphiHookTxt: TEdit;
    imgPreview: TImage;
    trcPreview: TTrackBar;
    btnStartTime: TButton;
    btnEndTime: TButton;
    lblPTS: TLabel;
    grpVideoFilter: TGroupBox;
    chkVertFlip: TCheckBox;
    chkHoriFlip: TCheckBox;
    edtOutPath: TEdit;
    lblOutPath: TLabel;
    btnOutPath: TButton;
    chkNegate: TCheckBox;
    chkRotate: TCheckBox;
    edtRotate: TEdit;
    chkScale: TCheckBox;
    edtScale: TEdit;
    chkSetPTS: TCheckBox;
    edtSetPTS: TEdit;
    grpPadCrop: TGroupBox;
    lblTop: TLabel;
    lblBottom: TLabel;
    lblLeft: TLabel;
    lblRight: TLabel;
    chkCrop: TCheckBox;
    edtCropTop: TEdit;
    edtCropBottom: TEdit;
    edtCropLeft: TEdit;
    edtCropRight: TEdit;
    chkPad: TCheckBox;
    edtPadTop: TEdit;
    edtPadBottom: TEdit;
    edtPadLeft: TEdit;
    edtPadRight: TEdit;
    procedure chkAudioVolumeClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure chkVideoBitRateClick(Sender: TObject);
    procedure chkAudioBitRateClick(Sender: TObject);
    procedure btnPTSsClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure trcPreviewChange(Sender: TObject);
    procedure btnStartTimeClick(Sender: TObject);
    procedure btnEndTimeClick(Sender: TObject);
    procedure DoUpdateHook(Sender: TObject);
    procedure imgPreviewClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormHide(Sender: TObject);
    procedure btnOutPathClick(Sender: TObject);
    procedure chkRotateClick(Sender: TObject);
    procedure chkScaleClick(Sender: TObject);
    procedure chkSetPTSClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure chkCropClick(Sender: TObject);
    procedure chkPadClick(Sender: TObject);
  private
    FAVProbe: TAVProbe;
    FTraceBarUpdating: Boolean;
    FBitmap: TBitmap;
    FPosFactor: Int64;
    FVideoStreamIndex: Integer;
    FAudioStreamIndex: Integer;
    procedure DoActualFormClick(Sender: TObject);
    procedure UpdateHook;
    procedure DrawFramePicture;
    function GetClipStartTime: Integer;
    function GetClipTimeLength: Integer;
    function GetInputOptions: TInputOptions;
    function GetOutputOptions: TOutputOptions;
    procedure SetAVProbe(const Value: TAVProbe);
  public
    property AVProbe: TAVProbe read FAVProbe write SetAVProbe;
    property InputOptions: TInputOptions read GetInputOptions;
    property OutputOptions: TOutputOptions read GetOutputOptions;
  end;

//var
//  frmOption: TfrmOption;

implementation

{$R *.dfm}

uses
  ShlObj,
  ActiveX;

const
  SHookText = 'FFVCL';
  CHookPath = 'vhook\';
  CImlib2DLL = 'imlib2.dll';
  CImlib2Img = 'imlib2.jpg';
  CImlib2DepDLL = 'libImlib2-1.dll';
  CFreeTypeDLL = 'libfreetype-6.dll';
  CWaterMarkDLL = 'watermark.dll';
  CWaterMarkImg = 'watermark.gif';
  CDelphiHookDLL= 'DelphiHook.dll';
  CDelphiHookImg = 'DelphiHook.bmp';

  SSameAsInput = '<same as input>';
  SViewActual = 'Click to view current picture in actual size';
  SHelpPTSCaption = 'Video Hook PTS Periods';
  SHelpPTSText =
    'Enable Video Hook only in special periods, empty means whole duration enabled.'#13#10 +
    'format: [<pts1>-<pts2>[,pts3-pts4[,...]]]'#13#10 +
    'example: to process video hook only in pts periods 0-5,10-15 (seconds)'#13#10 +
    '         PTS Periosd(millisecond!!!) -> "0-5000,10000-15000"';

{ Utils }

function FormatSeconds(const AMicroSeconds: Int64): string;
begin
  Result := Format('%d.%.3d', [AMicroSeconds div 1000000, AMicroSeconds div 1000 mod 1000]);
end;

// scale source rect into destination rect
function FitRect(ASrcRect, ADestRect: TRect): TRect;
var
  LWFactor: Double;
  LHFactor: Double;
  LSpace: Integer;
begin
  LWFactor := (ASrcRect.Right - ASrcRect.Left) / (ADestRect.Right - ADestRect.Left);
  LHFactor := (ASrcRect.Bottom - ASrcRect.Top) / (ADestRect.Bottom - ADestRect.Top);
  if LWFactor < LHFactor then
  begin
    Result.Top := ADestRect.Top;
    Result.Bottom := ADestRect.Bottom;
    LSpace := Round(((ADestRect.Right - ADestRect.Left) - (ASrcRect.Right - ASrcRect.Left) / LHFactor) / 2);
    Result.Left := ADestRect.Left + LSpace;
    Result.Right := ADestRect.Right - LSpace;
  end
  else
  begin
    Result.Left := ADestRect.Left;
    Result.Right := ADestRect.Right;
    LSpace := Round(((ADestRect.Bottom - ADestRect.Top) - (ASrcRect.Bottom - ASrcRect.Top) / LWFactor) / 2);
    Result.Top := ADestRect.Top + LSpace;
    Result.Bottom := ADestRect.Bottom - LSpace;
  end;
end;

// stretch draw bitmap on image with fit scale
procedure FitDraw(ACanvas: TCanvas; ABitmap: TBitmap);
var
  R: TRect;
begin
  if ABitmap.Width * ABitmap.Height = 0 then Exit;
//{TODO:Debug only, remove when release}AImage.Canvas.Draw(0,0,ABitmap);Exit;
  R := ACanvas.ClipRect;
  // calculate scaled rect then draw bitmap on image
{$IFDEF DRAW_FRAME}
  InflateRect(R, -1, -1);
{$ENDIF}
  R := FitRect(ABitmap.Canvas.ClipRect, R);
  ACanvas.StretchDraw(R, ABitmap);
{$IFDEF DRAW_FRAME}
  AImage.Canvas.Brush.Color := clGreen;
  InflateRect(R, 1, 1);
  AImage.Canvas.FrameRect(R);
{$ENDIF}
end;

function SelectDirCBEx(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall;
var
  LRect: TRect;
  cx, cy, w, h, x, y: Integer;
begin
  if (uMsg = BFFM_INITIALIZED) then
  begin
    if (lpData <> 0) then
      SendMessage(Wnd, BFFM_SETSELECTION, Integer(True), lpData);
    cx := GetSystemMetrics(SM_CXSCREEN);
    cy := GetSystemMetrics(SM_CYSCREEN);
    GetWindowRect(Wnd, LRect);
    w := LRect.Right - LRect.Left;
    h := LRect.Bottom - LRect.Top;
    x := (cx - w) div 2;
    y := (cy - h) div 2;
    SetWindowPos(Wnd, 0, x, y, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
  end;
  Result := 0;
end;

function SelectDirectoryEx(const Caption: string; const Root: WideString; var Directory: string): Boolean;
{$IFDEF VER140}
const
  BIF_NEWDIALOGSTYLE = $0040;
  BIF_USENEWUI = BIF_NEWDIALOGSTYLE or BIF_EDITBOX;
{$ENDIF}
var
  WindowList: Pointer;
  BrowseInfo: TBrowseInfo;
  Buffer: PChar;
  OldErrorMode: Cardinal;
  RootItemIDList, ItemIDList: PItemIDList;
  ShellMalloc: IMalloc;
  IDesktopFolder: IShellFolder;
  Eaten, Flags: LongWord;
begin
  Result := False;
  if not DirectoryExists(Directory) then
    Directory := '';
  Directory := ExpandFileName(Directory);
  FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
  if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
  begin
    Buffer := ShellMalloc.Alloc(MAX_PATH);
    try
      RootItemIDList := nil;
      if Root <> '' then
      begin
        SHGetDesktopFolder(IDesktopFolder);
        IDesktopFolder.ParseDisplayName(Application.Handle, nil,
          POleStr(Root), Eaten, RootItemIDList, Flags);
      end;
      with BrowseInfo do
      begin
        hwndOwner := Application.Handle;
        pidlRoot := RootItemIDList;
        pszDisplayName := Buffer;
        lpszTitle := PChar(Caption);
        ulFlags := BIF_RETURNONLYFSDIRS or BIF_DONTGOBELOWDOMAIN or BIF_USENEWUI;
        lpfn := SelectDirCBEx;
        if Directory <> '' then
          lParam := Integer(PChar(Directory));
      end;
      WindowList := DisableTaskWindows(0);
      OldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
      try
        ItemIDList := ShBrowseForFolder(BrowseInfo);
      finally
        SetErrorMode(OldErrorMode);
        EnableTaskWindows(WindowList);
      end;
      Result := ItemIDList <> nil;
      if Result then
      begin
        ShGetPathFromIDList(ItemIDList, Buffer);
        ShellMalloc.Free(ItemIDList);
        if Buffer <> '' then
          Directory := IncludeTrailingPathDelimiter(Buffer)
        else
          Result := False;
      end;
    finally
      ShellMalloc.Free(Buffer);
    end;
  end;
end;

{ TfrmOption }

procedure TfrmOption.FormCreate(Sender: TObject);
begin
  imgPreview.Hint := SViewActual;
  imgPreview.ShowHint := True;
  edtImlib2Txt.Text := SHookText;
  edtDelphiHookTxt.Text := SHookText;
  edtOutPath.Text := SSameAsInput;
  FBitmap := TBitmap.Create;
end;

procedure TfrmOption.FormDestroy(Sender: TObject);
begin
  FBitmap.Free;
end;

procedure TfrmOption.FormShow(Sender: TObject);
begin
  UpdateHook;

  if grpFormats.CanFocus and (grpFormats.ItemIndex >= 0) then
{$IFDEF VER140}
    grpFormats.SetFocus;
{$ELSE}
    grpFormats.Buttons[grpFormats.ItemIndex].SetFocus;
{$ENDIF}
end;

procedure TfrmOption.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  LDriveType: UINT;
begin
  if Self.ModalResult = mrOk then
  begin
    if edtOutPath.Text = SSameAsInput then
      LDriveType := GetDriveType(PChar(ExtractFileDrive(FAVProbe.FileName)))
    else
      LDriveType := GetDriveType(PChar(ExtractFileDrive(edtOutPath.Text)));

    if LDriveType = DRIVE_CDROM then
    begin
      Application.MessageBox('Input folder is located in CD-ROM, please select output folder.',
        PChar(Application.Title), MB_ICONINFORMATION);
      btnOutPath.SetFocus;
      CanClose := False;
    end;
  end;
end;

procedure TfrmOption.FormHide(Sender: TObject);
begin
  FTraceBarUpdating := True;
  try
    trcPreview.Max := 0;
  finally
    FTraceBarUpdating := False;
  end;
end;

procedure TfrmOption.DrawFramePicture;
begin
  // function SeekFrame(const APTS: Int64; const AStreamIndex: Integer = -1): Boolean;
  //   APTS: Presentation Time Stamp in microsecond, its bound is 0 to
  //     total duration of the video file or special stream.
  //   AStreamIndex: special stream to seek, normally set as video stream's index.
  //     -1 means using the first video stream.

  // function ReadFramePicture(const AStreamIndex: Integer = -1): TBitmap;
  //   AStreamIndex: special video stream to seek, must set as video stream's index.
  //     -1 means using the first video stream.
  //   return current frame picture of the special video stream as bitmap.
  //   NOTICE: after ReadFramePicture() call, the position will change to next frame.
  //     so if you want it again, you should call SeekFrame() before call ReadFramePicture().
  //     alternative, you can use property "FramePicture" to access the picture cached
  //     in last successful ReadFramePicture() call.

  if not FTraceBarUpdating and (FAVProbe.VideoStreamCount > 0) then
  begin
    // seek to the frame according the trace bar position
    if trcPreview.Position = trcPreview.Max then
      FAVProbe.SeekFrame(FAVProbe.FileStreamInfo.Duration, FVideoStreamIndex)
    else
      FAVProbe.SeekFrame(trcPreview.Position * FPosFactor, FVideoStreamIndex);
    // read frame picture and draw it on image with fit scale.
    FitDraw(imgPreview.Canvas, FAVProbe.ReadFramePicture(FVideoStreamIndex));
    FBitmap.Assign(FAVProbe.FramePicture);
  end;
  if not FTraceBarUpdating then
    // update current pts
    lblPTS.Caption := 'PTS: ' + FormatSeconds(trcPreview.Position * FPosFactor);
end;

procedure TfrmOption.SetAVProbe(const Value: TAVProbe);
  procedure SetupTraceBar;
  var
    LPosMax: Int64;
    LLineSize: Integer;
  begin
    // "image2" format cannot to be seeked
    trcPreview.SliderVisible := (FAVProbe.FormatName <> 'image2'); {Do not Localize};
    FTraceBarUpdating := True;
    try
      trcPreview.SelStart := 0;
      trcPreview.SelEnd := 0;
      chkCutClip.Enabled := False;
      chkCutClip.Checked := False;
      edtStartTime.Text := 'N/A';
      edtEndTime.Text := 'N/A';
      if trcPreview.SliderVisible then
      begin
        FPosFactor := 1;
        repeat
          FPosFactor := FPosFactor * 10;
          LPosMax := FAVProbe.FileStreamInfo.Duration div FPosFactor;
        until LPosMax < $8000;
        trcPreview.Frequency := LPosMax div ((trcPreview.Width - 12) div 10);
        trcPreview.Min := 0;
        trcPreview.Max := LPosMax;
        if LPosMax > 10000 then
          LLineSize := 1000
        else if LPosMax > 1000 then
          LLineSize := 100
        else if LPosMax > 100 then
          LLineSize := 10
        else
          LLineSize := 1;
        trcPreview.LineSize := LLineSize;
        trcPreview.PageSize := LLineSize * 5;
        trcPreview.Position := 0;
        trcPreview.TickStyle := tsAuto;
      end
      else
      begin
        trcPreview.Min := 0;
        trcPreview.Max := 0;
      end;
    finally
      FTraceBarUpdating := False;
    end;
  end;
begin
  // clear frame picture
  FBitmap.Width := 0;
  FBitmap.Height := 0;
  // fill background
  imgPreview.Canvas.Brush.Color := clSkyBlue;
  imgPreview.Canvas.FillRect(imgPreview.Canvas.ClipRect);

  FAVProbe := Value;

  // NOTICE: this deom only shows first video stream and/or first audio stream.
  FVideoStreamIndex := FAVProbe.FirstVideoStreamIndex;
  FAudioStreamIndex := FAVProbe.FirstAudioStreamIndex;

  // setup the trace bar to fit duration
  SetupTraceBar;

  // draw frame picture
  DrawFramePicture;

  // display AVFileInfo
  mmoAVFileInfo.Text := FAVProbe.FileInfoText;

  // TODO: here you can show more special AVFileInfo in detail and
  //  initialize preset options according the input file
end;

procedure TfrmOption.UpdateHook;
var
  LVideoHookPath: string;
begin
  // check video hook dll
  LVideoHookPath := ExtractFilePath(Application.ExeName) + CHookPath;
  // Video Hook DLL: Imglib2
  chkImlib2Img.Enabled := rdoExternalHook.Checked and
    FileExists(LVideoHookPath + CImlib2DLL) and
    FileExists(LVideoHookPath + CImlib2DepDLL) and
    FileExists(LVideoHookPath + CFreeTypeDLL);
  chkImlib2Txt.Enabled := chkImlib2Img.Enabled;
  // Video Hook DLL: WaterMark
  chkWaterMark.Enabled := rdoExternalHook.Checked and FileExists(LVideoHookPath + CWaterMarkDLL);
  // Video Hook DLL: DelphiHook
  chkDelphiHookImg.Enabled := rdoExternalHook.Checked and FileExists(LVideoHookPath + CDelphiHookDLL);
  chkDelphiHookTxt.Enabled := chkDelphiHookImg.Enabled;

  chkImgDancing.Enabled := chkImlib2Img.Enabled and chkImlib2Img.Checked;
  edtImlib2Txt.Enabled := chkImlib2Txt.Enabled and chkImlib2Txt.Checked;
  chkTxtScrolling.Enabled := chkImlib2Txt.Enabled and chkImlib2Txt.Checked;
  chkWaterMarkMode.Enabled := chkWatermark.Enabled and chkWatermark.Checked;
  edtDelphiHookTxt.Enabled := chkDelphiHookTxt.Enabled and chkDelphiHookTxt.Checked;

  rdoStandardHook.Enabled := (chkImlib2Img.Enabled and chkImlib2Img.Checked) or
                             (chkImlib2Txt.Enabled and chkImlib2Txt.Checked) or
                             (chkWaterMark.Enabled and chkWaterMark.Checked) or
                             (chkDelphiHookImg.Enabled and chkDelphiHookImg.Checked) or
                             (chkDelphiHookTxt.Enabled and chkDelphiHookTxt.Checked);
  rdoBeforeHook.Enabled := rdoStandardHook.Enabled;
  rdoPTSs.Enabled := rdoStandardHook.Enabled;

  edtPTSs.Enabled := rdoPTSs.Enabled and rdoPTSs.Checked;
end;

procedure TfrmOption.chkAudioVolumeClick(Sender: TObject);
begin
  edtAudioVolume.Enabled := chkAudioVolume.Checked;
end;

procedure TfrmOption.chkCropClick(Sender: TObject);
begin
  edtCropLeft.Enabled := chkCrop.Checked;
  edtCropRight.Enabled := chkCrop.Checked;
  edtCropTop.Enabled := chkCrop.Checked;
  edtCropBottom.Enabled := chkCrop.Checked;
end;

procedure TfrmOption.chkPadClick(Sender: TObject);
begin
  edtPadLeft.Enabled := chkPad.Checked;
  edtPadRight.Enabled := chkPad.Checked;
  edtPadTop.Enabled := chkPad.Checked;
  edtPadBottom.Enabled := chkPad.Checked;
end;

procedure TfrmOption.chkRotateClick(Sender: TObject);
begin
  edtRotate.Enabled := chkRotate.Checked;
end;

procedure TfrmOption.chkScaleClick(Sender: TObject);
begin
  edtScale.Enabled := chkScale.Checked;
end;

procedure TfrmOption.chkSetPTSClick(Sender: TObject);
begin
  edtSetPTS.Enabled := chkSetPTS.Checked;
end;

procedure TfrmOption.chkAudioBitRateClick(Sender: TObject);
begin
  edtAudioBitRate.Enabled := chkAudioBitRate.Checked;
end;

procedure TfrmOption.chkVideoBitRateClick(Sender: TObject);
begin
  edtVideoBitRate.Enabled := chkVideoBitRate.Checked;
end;

procedure TfrmOption.DoUpdateHook(Sender: TObject);
begin
  UpdateHook;
end;

procedure TfrmOption.btnOutPathClick(Sender: TObject);
var
  LPath: string;
begin
  if edtOutPath.Text = SSameAsInput then
    LPath := ExtractFilePath(FAVProbe.FileName)
  else
    LPath := edtOutPath.Text;
  if SelectDirectoryEx('Select Output Folder', '', LPath) then
    if GetDriveType(PChar(ExtractFileDrive(LPath))) = DRIVE_CDROM then
      Application.MessageBox('Output folder cannot be located in CD-ROM.',
        PChar(Application.Title), MB_ICONINFORMATION)
    else
      edtOutPath.Text := LPath;
end;

procedure TfrmOption.btnPTSsClick(Sender: TObject);
begin
  Application.MessageBox(SHelpPTSText, SHelpPTSCaption, MB_ICONINFORMATION);
end;

procedure TfrmOption.DoActualFormClick(Sender: TObject);
begin
  if Assigned(Sender) and (Sender is TControl) and ((Sender as TControl).Parent is TForm) then
    ((Sender as TControl).Parent as TForm).Close;
end;

procedure TfrmOption.imgPreviewClick(Sender: TObject);

var
  F: TForm;
  I: TImage;
  B: TButton;
begin
  if FBitmap.Width * FBitmap.Height = 0 then Exit;

  // show current frame picture in actual size
  F := TForm.Create(Self);
  try
    F.BorderIcons := [biSystemMenu];
    B := TButton.Create(F);
    B.Parent := F;
    B.Default := True;
    B.Cancel := True;
    B.ModalResult := mrCancel;
    B.Top := 0;
    B.Left := 0;
    B.Width := 0;
    B.Height := 0;
    B.Visible := True;
    I := TImage.Create(F);
    I.Parent := F;
    I.OnClick := Self.DoActualFormClick;
    I.Visible := True;
    I.Left := 0;
    I.Top := 0;
    I.Width := FBitmap.Width;
    I.Height := FBitmap.Height;
    I.Picture.Assign(FBitmap);
    F.Position := poScreenCenter;
    F.BorderStyle := bsSingle;
    F.Caption := Format('%dx%d PTS: %s', [FBitmap.Width, FBitmap.Height,
      FormatSeconds(trcPreview.Position * FPosFactor)]);;
    F.ClientWidth := I.Width;
    F.ClientHeight := I.Height;
    F.ShowModal;
  finally
    F.Free;
  end;
end;

procedure TfrmOption.trcPreviewChange(Sender: TObject);
begin
  DrawFramePicture;
end;

procedure TfrmOption.btnStartTimeClick(Sender: TObject);
begin
  trcPreview.SelStart := trcPreview.Position;
  edtStartTime.Text := FormatSeconds(trcPreview.Position * FPosFactor);

  chkCutClip.Enabled := (trcPreview.SelEnd > 0) and (trcPreview.SelEnd > trcPreview.SelStart);
end;

procedure TfrmOption.btnEndTimeClick(Sender: TObject);
begin
  trcPreview.SelEnd := trcPreview.Position;
  edtEndTime.Text := FormatSeconds(trcPreview.Position * FPosFactor);

  chkCutClip.Enabled := (trcPreview.SelEnd > 0) and (trcPreview.SelEnd > trcPreview.SelStart);
end;

{******************************************
          time line
  |-----------------------------|
  ^     ^            ^          ^
 zero   |            |       total duration
      StartTime    EndTime
      (offset)     (offset)
        |------------|
         \          /
          TimeLength
        (new duration)
******************************************}

procedure CheckClipTime(const ATotalDuration: Integer; var AStartTime, AEndTime: Integer);
begin
  if (AStartTime < 0) or (AStartTime >= ATotalDuration) then
    AStartTime := 0; // set to zero if StartTime is invalid
  if (AEndTime <= AStartTime) or (AEndTime > ATotalDuration) then
    AEndTime := ATotalDuration; // set to total duration if EndTime is invalid
end;

function TfrmOption.GetClipStartTime: Integer;
var
  LStartTime: Integer;
  LEndTime: Integer;
begin
  LStartTime := trcPreview.SelStart * FPosFactor div 1000; // milliseconds!!!
  LEndTime := trcPreview.SelEnd * FPosFactor div 1000;     // milliseconds!!!
  CheckClipTime(FAVProbe.FileStreamInfo.Duration div 1000, LStartTime, LEndTime);

  Result := LStartTime; // start time offset
end;

function TfrmOption.GetClipTimeLength: Integer;
var
  LStartTime: Integer;
  LEndTime: Integer;
begin
  LStartTime := trcPreview.SelStart * FPosFactor div 1000; // milliseconds!!!
  LEndTime := trcPreview.SelEnd * FPosFactor div 1000;     // milliseconds!!!
  CheckClipTime(FAVProbe.FileStreamInfo.Duration div 1000, LStartTime, LEndTime);

  Result := LEndTime - LStartTime; // new duration of the new piece clip
end;

function TfrmOption.GetInputOptions: TInputOptions;
var
  IO: TInputOptions;
begin
  // please refer to the relational document for detail information of TInputOptions

  InitInputOptions(@IO); // Initialize empty options

  IO.FileName := FAVProbe.FileName;
  IO.FileFormat := FAVProbe.ForceFormat; // !!!*RECOMMEND*!!!

  // cut a piece clip, see also OO.TimeLength
  if chkCutClip.Enabled and chkCutClip.Checked and
    (trcPreview.SelEnd > 0) and (trcPreview.SelEnd > trcPreview.SelStart) then
    IO.TimeStart := GetClipStartTime; // start time offset

  // IO.ExtOptions usage: extent options for flexibility
  // Format: name1=value1<CRLF>name2=value2<CRLF>...nameN=valueN<CRLF>
  //         name and value correspond to ffmpeg.exe's parameters
  //         e.g. "pix_fmt=yuv422p<CRLF>aspect=16:9<CRLF>"
  // you can use this options according to your special purpose

  if Pos(LowerCase(grpFormats.Items.Strings[grpFormats.ItemIndex]),
    'aac ac3 mp2 mp3 wav wma') > 0 then
    IO.ExtOptions := 'vn=1'; // disable input video

  Result := IO;
end;

function TfrmOption.GetOutputOptions: TOutputOptions;
var
  OO: TOutputOptions;
  LFileExt: string;
  LTemp: string;
  LBaseName: string;
  LVideoCodec: string;
  LAudioCodec: string;
  I: Integer;
  LHookDLL: string;
  LHookImg: string;
  LDelimiter: string;
  X, Y: string;
  LBitmap: TBitmap;
begin
  // please refer to the relational document for detail information of TOutputOptions

  InitOutputOptions(@OO); // Initialize empty options

  // here we use FileExt to indicate the output format
  LFileExt := '.' + LowerCase(grpFormats.Items.Strings[grpFormats.ItemIndex]);

  // Audio Channels
  if grpAudioChannels.ItemIndex > 0 then
    OO.AudioChannels := grpAudioChannels.ItemIndex;

  // Audio Sample Rate: Hz value
  if grpAudioSampleRate.ItemIndex > 0 then
    OO.AudioSampleRate :=
      StrToIntDef(grpAudioSampleRate.Items.Strings[grpAudioSampleRate.ItemIndex], 22050);

  // Audio Volume
  if chkAudioVolume.Checked then
    OO.AudioVolume := StrToIntDef(edtAudioVolume.Text, -1);

  // Video Frame Size: WxH or abbreviation
  if cboFrameSize.ItemIndex > 0 then
  begin
    LTemp := cboFrameSize.Items.Strings[cboFrameSize.ItemIndex];
    if Pos(' ', LTemp) > 1 then
      LTemp := Copy(LTemp, 1, Pos(' ', LTemp) - 1);
    OO.FrameSize := LTemp;
  end;

  // Frame Rate(FPS): Hz value, fraction or abbreviation
  if grpFrameRate.ItemIndex > 0 then
    OO.FrameRate := grpFrameRate.Items.Strings[grpFrameRate.ItemIndex];

  // Aspect Ratio: 4:3, 16:9 or 1.3333, 1.7777
  if grpAspectRatio.ItemIndex > 0 then
    OO.FrameAspectRatio := grpAspectRatio.Items.Strings[grpAspectRatio.ItemIndex];

  // Video Bit Rate: in bits/s, bps!!! not Kbps!!! so multiply 1000
  if chkVideoBitRate.Checked then
    OO.VideoBitrate := 1000 * StrToIntDef(edtVideoBitRate.Text, 200)
  else
  begin
    // NOTICE: by default, ffmpeg will make video bitrate as 200*1000, so here if you want to
    //    keep the original value, you must explicitly set it.
    // please refer to ffmpeg's libavcodec/utils.c, "b" of options[], AV_CODEC_DEFAULT_BITRATE
    // example to keep the original video bitrate
    // OO.VideoBitrate := FAVProbe.VideoStreamInfos[FVideoStreamIndex].BitRate;
  end;

  // Audio Bit Rate: in bits/s, bps!!! not Kbps!!! so multiply 1000
  if chkAudioBitRate.Checked then
    OO.AudioBitrate := 1000 * StrToIntDef(edtAudioBitRate.Text, 64)
  else
  begin
    // NOTICE: by default, ffmpeg will make audio bitrate as 64*1000, so here if you want to
    //    keep the original value, you must explicitly set it.
    // please refer to ffmpeg's libavcodec/utils.c, "ab" of options[]
    // example to keep the original audio bitrate
    // OO.AudioBitrate := FAVProbe.AudioStreamInfos[FAudioStreamIndex].BitRate;
  end;

  // Video Codec(Encoder)
  if grpVideoCodec.ItemIndex > 0 then
  begin
    LVideoCodec := LowerCase(grpVideoCodec.Items.Strings[grpVideoCodec.ItemIndex]);
    if LVideoCodec = 'divx' then
    begin
      OO.VideoCodec := 'mpeg4';      {Do not Localize}
      OO.VideoTag := 'DIVX';         {Do not Localize}
    end
    else if LVideoCodec = 'xvid' then
      OO.VideoCodec := 'libxvid'      {Do not Localize}
    else if LVideoCodec = 'x264' then
      OO.VideoCodec := 'libx264';     {Do not Localize}
    // TODO: add more video codecs option
  end;

  // Audio Codec(Encoder)
  if grpAudioCodec.ItemIndex > 0 then
  begin
    LAudioCodec := LowerCase(grpAudioCodec.Items.Strings[grpAudioCodec.ItemIndex]);
    if LAudioCodec = 'aac' then
      OO.AudioCodec := 'libfaac'      {Do not Localize}
    else if LAudioCodec = 'ac3' then
      OO.AudioCodec := 'ac3'          {Do not Localize}
    else if LAudioCodec = 'flac' then
      OO.AudioCodec := 'flac'         {Do not Localize}
    else if LAudioCodec = 'mp3' then
      OO.AudioCodec := 'libmp3lame';  {Do not Localize}
    // TODO: add more audio codecs option
  end;

  // cut a piece clip, see also IO.StartTime
  if chkCutClip.Enabled and chkCutClip.Checked and
    (trcPreview.SelEnd > 0) and (trcPreview.SelEnd > trcPreview.SelStart) then
    OO.TimeLength := GetClipTimeLength; // duration of the new piece clip

  // **********Below code shows Video Hook usage*************

  // NOTICE: only one of the (CustomHook, BeforeHook, VideoHookPTSs) options will act in the same time.
  //     they come into effect in this priority order: CustomHook, BeforeHook, VideoHookPTSs

  // here we use ExtOptions to define vhook parameters
  // OO.ExtOptions usage: extent options for flexibility
  // Format: name1=value1<CRLF>name2=value2<CRLF>...nameN=valueN<CRLF>
  //         name and value correspond to ffmpeg.exe's parameters
  //         e.g. "pix_fmt=yuv422p<CRLF>aspect=16:9<CRLF>"

  // one or more vhooks can be defined in the same time.

  // Official Video Hook: please refer to http://ffmpeg.mplayerhq.hu/hooks.html
  // NOTICE: if vhook parameters(HookDLL or FileName) have SPACE characters,
  //         the delimiter between vhook parameters must be set to '|'.
  //         this feature is an increment to original FFmpeg's vhook.

  // Imlib2 Video Hook comes from official ffmpeg
  if rdoExternalHook.Checked and chkImlib2Img.Checked and chkImlib2Img.Enabled then
  begin // Imlib2 Hook (Image usage)
    LHookDLL := ExtractFilePath(Application.ExeName) + CHookPath + CImlib2DLL;
    LHookImg := ExtractFilePath(Application.ExeName) + CHookPath + CImlib2Img;
    if (Pos(' ', LHookDLL) > 0) or (Pos(' ', LHookImg) > 0) then
      LDelimiter := '|' {Do not Localize}
    else
      LDelimiter := ' ';
    if chkImgDancing.Checked then
    begin
//      X := 'W*(0.5+0.25*sin(N/47*PI))-w/2';
//      Y := 'H*(0.5+0.5*cos(N/97*PI))-h/2';
      X := '(W-w)*(0.5+0.5*sin(N/47*PI))';
      Y := '(H-h)*(0.5+0.5*cos(N/97*PI))';
    end
    else
    begin
      X := 'W-w';
      Y := 'H-h';
    end;
    OO.ExtOptions := OO.ExtOptions +
      Format('vhook=%s%s%s%s%s%s%s%s%s%s%s%s%s'#13#10, [
        LHookDLL, LDelimiter,
        '-i', LDelimiter, LHookImg, LDelimiter,
        '-x', LDelimiter, X, LDelimiter,
        '-y', LDelimiter, Y]);
  end;
  if rdoExternalHook.Checked and chkImlib2Txt.Checked and chkImlib2Txt.Enabled then
  begin // Imlib2 Hook (Text usage)
    LHookDLL := ExtractFilePath(Application.ExeName) + CHookPath + CImlib2DLL;
    if Pos(' ', LHookDLL) > 0 then
      LDelimiter := '|' {Do not Localize}
    else
      LDelimiter := ' ';
    if chkTxtScrolling.Checked then
    begin
      X := '10';
//      Y := 'abs(H-25-1.0*N)';
      Y := '(H-25)*(0.5+0.50*cos(N/97*PI))';
    end
    else
    begin
      X := '10';
      Y := '10';
    end;
    edtImlib2Txt.Text := Trim(edtImlib2Txt.Text);
    if edtImlib2Txt.Text = '' then
      edtImlib2Txt.Text := SHookText;
    OO.ExtOptions := OO.ExtOptions +
      Format('vhook=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s'#13#10, [
        LHookDLL, LDelimiter,
        '-R', LDelimiter, '255', LDelimiter,
        '-G', LDelimiter, '255', LDelimiter,
        '-B', LDelimiter, '255', LDelimiter,
        '-F', LDelimiter, 'Tahoma.ttf/16', LDelimiter,  {Imlib2 Hook need fontfile to draw text}
        '-t', LDelimiter, edtImlib2Txt.Text, LDelimiter,
        '-x', LDelimiter, X, LDelimiter,
        '-y', LDelimiter, Y]);
  end;

  // WaterMark Video Hook comes from official ffmpeg
  if rdoExternalHook.Checked and chkWaterMark.Checked and chkWaterMark.Enabled then
  begin
    LHookDLL := ExtractFilePath(Application.ExeName) + CHookPath + CWaterMarkDLL;
    LHookImg := ExtractFilePath(Application.ExeName) + CHookPath + CWaterMarkImg;
    if (Pos(' ', LHookDLL) > 0) or (Pos(' ', LHookImg) > 0) then
      LDelimiter := '|' {Do not Localize}
    else
      LDelimiter := ' ';
    OO.ExtOptions := OO.ExtOptions +
      Format('vhook=%s%s%s%s%s%s%s%s%d'#13#10, [
        LHookDLL, LDelimiter,
        '-f', LDelimiter, LHookImg, LDelimiter,
        '-m', LDelimiter, Ord(chkWaterMarkMode.Checked)]);
  end;

  // Delphi Video Hook: Draw Image Overlay and/or Draw Text Overlay
  //    it is written in Delphi Language.
  //    Delphi Source of DelphiHook.dll is available in the full source version component.
  //    Delphi Source of DelphiNull.dll is available in all version component.
  //    in fact, you can write your own Video Hook DLLs in any develop language.
  // Image Overlay parameters
  //    -i image filename (*.bmp 24b format)
  //    -ix image x
  //    -iy image y
  //    -mc RRGGBB (transparent color, optional)
  // Text Overlay parameters
  //    -t text
  //    -tx text x
  //    -ty text y
  //    -fc RRGGBB
  //    -fn fontname
  //    -fs fontsize
  //    -fst BIUS {one or all of Bold, Italic, Underline, StrikeOut}
  if rdoExternalHook.Checked and chkDelphiHookImg.Checked and chkDelphiHookImg.Enabled then
  begin // Delphi Hook (Image usage)
    LHookDLL := ExtractFilePath(Application.ExeName) + CHookPath + CDelphiHookDLL;
    LHookImg := ExtractFilePath(Application.ExeName) + CHookPath + CDelphiHookImg;
    LBitmap := TBitmap.Create;
    try
      if FileExists(LHookImg) then
        LBitmap.LoadFromFile(LHookImg)
      else
      begin
        LBitmap.Width := 64;
        LBitmap.Height := 64;
      end;
      if (Pos(' ', LHookDLL) > 0) or (Pos(' ', LHookImg) > 0) then
        LDelimiter := '|' {Do not Localize}
      else
        LDelimiter := ' ';
      OO.ExtOptions := OO.ExtOptions +
        Format('vhook=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s'#13#10, [
          LHookDLL, LDelimiter,
          '-i', LDelimiter, LHookImg, LDelimiter,
          '-mc', LDelimiter, 'FFFFFF', LDelimiter,
          '-ix', LDelimiter,
          IntToStr(FAVProbe.VideoStreamInfos[FVideoStreamIndex].Width - LBitmap.Width - 10), LDelimiter,
          '-iy', LDelimiter,
          IntToStr(FAVProbe.VideoStreamInfos[FVideoStreamIndex].Height - LBitmap.Height - 10)]);
    finally
      LBitmap.Free;
    end;
  end;
  if rdoExternalHook.Checked and chkDelphiHookTxt.Checked and chkDelphiHookTxt.Enabled then
  begin // Delphi Hook (Text usage)
    LHookDLL := ExtractFilePath(Application.ExeName) + CHookPath + CDelphiHookDLL;
    if Pos(' ', LHookDLL) > 0 then
      LDelimiter := '|' {Do not Localize}
    else
      LDelimiter := ' ';
    edtDelphiHookTxt.Text := Trim(edtDelphiHookTxt.Text);
    if edtDelphiHookTxt.Text = '' then
      edtDelphiHookTxt.Text := SHookText;
    OO.ExtOptions := OO.ExtOptions +
      Format('vhook=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s'#13#10, [
        LHookDLL, LDelimiter,
        '-t', LDelimiter, edtDelphiHookTxt.Text, LDelimiter,
        '-tx', LDelimiter, '10', LDelimiter,
        '-ty', LDelimiter, '10', LDelimiter,
        '-fc', LDelimiter, 'FFFFFF', LDelimiter,
        '-fn', LDelimiter, 'Tahoma', LDelimiter,
        '-fs', LDelimiter, '16', LDelimiter,
        '-fst', LDelimiter, 'BU']);
  end;

  // whether enable OnBeforeHook event, this feature is an increment to
  //        change the standard behavior of FFmpeg's vhook.
  OO.BeforeHook := rdoExternalHook.Checked and
    rdoBeforeHook.Checked and rdoBeforeHook.Enabled and
    (chkImlib2Img.Checked or chkImlib2Txt.Checked or chkWaterMark.Checked or
     chkDelphiHookImg.Checked or chkDelphiHookTxt.Checked);

  // whether enable VideoHookPTSs option, this feature is an increment to
  //        change the standard behavior of FFmpeg's vhook.
  if rdoExternalHook.Checked and rdoPTSs.Checked and rdoPTSs.Enabled and
    (chkImlib2Img.Checked or chkImlib2Txt.Checked or chkWaterMark.Checked or
     chkDelphiHookImg.Checked or chkDelphiHookTxt.Checked) then
  begin
    // enable Video Hook only in special periods, empty means disable this option.
    // format: [<pts1>-<pts2>[,pts3-pts4[,...]]]
    // example: to process video hook only in pts periods 0-5,10-15 (seconds)
    //          VideoHookPTS := '0-5000,10000-15000'; // millisecond!!!
    // TODO: you should check PTSs format
    OO.VideoHookPTSs := edtPTSs.Text;
  end;

  // whether enable OnCustomHook event, this feature is an increment to
  //        create your onw custom video hook behavior.
  OO.CustomHook := rdoCustomHook.Checked;

  // Copy Timestamp: solve the problem that audio does not synchronize with video
  OO.CopyTimestamp := chkCopyTimestamp.Checked;

  // **********Below code shows Video Filter usage*************

  // libavfilter: please refer to http://svn.mplayerhq.hu/soc/libavfilter/README?view=co
  // currently only several Video Filters are present.
  // NOTICE: What I know about libavfilter is also very limited.
  //         so you have to get help of it by yourself.

  // vflip: Vertically Flip Video Frame
  if chkVertFlip.Checked then
    if OO.VideoFilters <> '' then
      OO.VideoFilters := OO.VideoFilters + ',vflip' {Do not Localize}
    else
      OO.VideoFilters := 'vflip'; {Do not Localize}

  // hflip: Horizontally Flip Video Frame
  if chkHoriFlip.Checked then
    if OO.VideoFilters <> '' then
      OO.VideoFilters := OO.VideoFilters + ',hflip' {Do not Localize}
    else
      OO.VideoFilters := 'hflip'; {Do not Localize}

  // negate: Negate Video Frame
  if chkNegate.Checked then
    if OO.VideoFilters <> '' then
      OO.VideoFilters := OO.VideoFilters + ',negate' {Do not Localize}
    else
      OO.VideoFilters := 'negate'; {Do not Localize}

  // scale: Scale Video Frame, parameter: width:height
  if chkScale.Checked then
  begin
    LTemp := Trim(edtScale.Text);
    if LTemp <> '' then
    begin
      if OO.VideoFilters <> '' then
        OO.VideoFilters := OO.VideoFilters + ',scale=' + LTemp {Do not Localize}
      else
        OO.VideoFilters := 'scale=' + LTemp; {Do not Localize}
    end;
  end;

  // rotate: Rotate Video Frame, parameter: degree
  if chkRotate.Checked then
  begin
    LTemp := IntToStr(StrToIntDef(Trim(edtRotate.Text), 45));
    if OO.VideoFilters <> '' then
      OO.VideoFilters := OO.VideoFilters + ',rotate=' + LTemp {Do not Localize}
    else
      OO.VideoFilters := 'rotate=' + LTemp; {Do not Localize}
  end;

(*
  # A few usage examples follow, usable too as test scenarios.

  # Start counting PTS from zero
  ffmpeg -i input.avi -vfilters setpts=PTS-STARTPTS output.avi

  # Fast motion
  ffmpeg -i input.avi -vfilters setpts=0.5*PTS output.avi

  # Fixed rate 25 fps
  ffmpeg -i input.avi -vfilters setpts=N*AVTB/25 output.avi

  # Fixed rate 25 fps with some jitter
  ffmpeg -i input.avi -vfilters 'setpts=AVTB/25*(N+0.05*sin(N*2*PI/25))' output.avi
*)
  // setpts: Set PTS of Video Frame
  if chkSetPTS.Checked then
  begin
    LTemp := Trim(edtSetPTS.Text);
    if LTemp <> '' then
    begin
      if OO.VideoFilters <> '' then
        OO.VideoFilters := OO.VideoFilters + ',setpts=' + LTemp {Do not Localize}
      else
        OO.VideoFilters := 'setpts=' + LTemp; {Do not Localize}
    end;
  end;


  // special options depend on output formats
  if (LFileExt = '.flv') or (LFileExt = '.swf') then
  begin
    // FLV/SWF only supports this three sample rate, (44100, 22050, 11025).
    if (OO.AudioSampleRate <> 11025) and (OO.AudioSampleRate <> 22050)
      and (OO.AudioSampleRate <> 44100) then
      OO.AudioSampleRate := 22050;
  end
  else if LFileExt = '.ogg' then
  begin
    OO.VideoCodec := 'libtheora'; {Do not Localize}
    OO.AudioCodec := 'flac';      {Do not Localize}
  end
  else if LFileExt = '.psp' then
  begin
(*
please refer to http://ffmpeg.mplayerhq.hu/faq.html#SEC26

3.14 How do I encode videos which play on the PSP?
`needed stuff'
  -acodec libfaac -vcodec mpeg4 width*height<=76800 width%16=0 height%16=0
      -ar 24000 -r 30000/1001 or 15000/1001 -f psp
`working stuff'
  4mv, title
`non-working stuff'
  B-frames
`example command line'
  ffmpeg -i input -acodec libfaac -ab 128kb -vcodec mpeg4 -b 1200kb -ar 24000 -mbd 2 -flags +4mv+trell
      -aic 2 -cmp 2 -subcmp 2 -s 368x192 -r 30000/1001 -title X -f psp output.mp4
`needed stuff for H.264'
  -acodec libfaac -vcodec libx264 width*height<=76800 width%16=0? height%16=0? -ar 48000 -coder 1
      -r 30000/1001 or 15000/1001 -f psp
`working stuff for H.264'
  title, loop filter
`non-working stuff for H.264'
  CAVLC
`example command line'
  ffmpeg -i input -acodec libfaac -ab 128kb -vcodec libx264 -b 1200kb -ar 48000 -mbd 2 -coder 1
      -cmp 2 -subcmp 2 -s 368x192 -r 30000/1001 -title X -f psp -flags loop -trellis 2
      -partitions parti4x4+parti8x8+partp4x4+partp8x8+partb8x8 output.mp4
`higher resolution for newer PSP firmwares, width<=480, height<=272'
  -vcodec libx264 -level 21 -coder 1 -f psp
`example command line'
  ffmpeg -i input -acodec libfaac -ab 128kb -ac 2 -ar 48000 -vcodec libx264 -level 21 -b 640kb -coder 1
      -f psp -flags +loop -trellis 2 -partitions +parti4x4+parti8x8+partp4x4+partp8x8+partb8x8
      -g 250 -s 480x272 output.mp4
*)
    OO.FileFormat := 'psp';       {Do not Localize}
    OO.VideoCodec := 'mpeg4';     {Do not Localize}
    OO.AudioCodec := 'libfaac';   {Do not Localize}
    OO.AudioSampleRate := 24000;
    OO.FrameRate := '15000/1001';
    LFileExt := '_psp.mp4';
  end
  else if LFileExt = '.ipod' then
  begin
(*
please refer to http://ffmpeg.mplayerhq.hu/faq.html#SEC25

3.13 How do I encode videos which play on the iPod?
`needed stuff'
  -acodec libfaac -vcodec mpeg4 width<=320 height<=240
`working stuff'
  4mv, title
`non-working stuff'
  B-frames
`example command line'
  ffmpeg -i input -acodec libfaac -ab 128kb -vcodec mpeg4 -b 1200kb -mbd 2 -flags +4mv+trell
      -aic 2 -cmp 2 -subcmp 2 -s 320x180 -title X output.mp4
*)
    OO.VideoCodec := 'mpeg4';     {Do not Localize}
    OO.AudioCodec := 'libfaac';   {Do not Localize}
    OO.FrameSize := '320x240';
    LFileExt := '_ipod.mp4';
  end
  else if LFileExt = '.rm10' then
  begin
    OO.VideoCodec := 'rv10';      {Do not Localize}
    LFileExt := '_rv10.rm';
  end
  else if LFileExt = '.rm20' then
  begin
    OO.VideoCodec := 'rv20';      {Do not Localize}
    LFileExt := '_rv20.rm';
  end
  else if LFileExt = '.mp4' then
  begin
{
    OO.AudioChannels := 1;
    OO.AudioSampleRate := 16000;
    OO.FrameRate := '15';
    OO.FrameAspectRatio := '1.222';
}
  end
  else if LFileExt = '.wmv' then
  begin
    OO.VideoCodec := 'wmv2';      {Do not Localize}
    OO.AudioCodec := 'wmav2';     {Do not Localize}
    //OO.VideoBitrate := 256 * 1024;
  end
  else if LFileExt = '.mkv' then
  begin
    OO.VideoCodec := 'libx264';   {Do not Localize}
    //OO.AudioCodec := 'ac3';       {Do not Localize}
  end
  else if LFileExt = '.wma' then
  begin
    OO.DisableVideo := True;
    OO.AudioCodec := 'wmav2';     {Do not Localize}
    //OO.VideoBitrate := 256 * 1024;
  end
  else if LFileExt = '.3gp' then
  begin
    // [libamr_nb @ 6BF9AE00]Only mono supported
    OO.AudioChannels := 1;
    // [libamr_nb @ 6BF9AE00]Only 8000Hz sample rate supported
    OO.AudioSampleRate := 8000;
    // [libamr_nb @ 6BF9AE00]bitrate not supported: use one of
    //    4.75k, 5.15k, 5.9k, 6.7k, 7.4k, 7.95k, 10.2k or 12.2k
(*
    AMR_bitrates rates[]={ {4750,MR475},
                           {5150,MR515},
                           {5900,MR59},
                           {6700,MR67},
                           {7400,MR74},
                           {7950,MR795},
                           {10200,MR102},
                           {12200,MR122},
                         };
*)
    OO.AudioBitrate := 7400;
    // [h263 @ 6BF9AE00]The specified picture size of 320x240 is not valid for the H.263 codec.
    //    Valid sizes are 128x96, 176x144, 352x288, 704x576, and 1408x1152. Try H.263+.
    OO.FrameSize := '128x96';
  end
  else if Pos(LFileExt, '.vcd.svcd.dvd') > 0 then
  begin
    if LFileExt = '.vcd' then
      OO.TargetType := ttVCD
    else if LFileExt = '.svcd' then
      OO.TargetType := ttSVCD
    else // if Ext = '.dvd' then
      OO.TargetType := ttDVD;

    if grpNorm.ItemIndex = 0 then
      OO.NormType := ntAuto
    else if grpNorm.ItemIndex = 1 then
      OO.NormType := ntPAL
    else
      OO.NormType := ntNTSC;

    OO.NormDefault := ntPAL; // or ntNTSC, according the locale region
  end;

  OO.Info.TimeStamp := 0; // 0 means current time: Now()
{
  // FFmpeg only supports IDv1
  OO.Info.Title := 'cc title';
  OO.Info.Author := 'cc author';
  OO.Info.Copyright := 'cc copyright';
  OO.Info.Comment := 'cc comment';
  OO.Info.Album := 'cc album';
  OO.Info.Year := 2008;
  OO.Info.Track := 5;
  OO.Info.Genre := 'Dance Hall';
}

  if chkCrop.Checked then
  begin
    // crop size must be a multiple of 2
    OO.CropTop := StrToIntDef(edtCropTop.Text, 0) div 2 * 2;
    OO.CropBottom := StrToIntDef(edtCropBottom.Text, 0) div 2 * 2;
    OO.CropLeft := StrToIntDef(edtCropLeft.Text, 0) div 2 * 2;
    OO.CropRight := StrToIntDef(edtCropRight.Text, 0) div 2 * 2;
  end;

  if chkPad.Checked then
  begin
    // pad size must be a multiple of 2
    OO.PadTop := StrToIntDef(edtPadTop.Text, 0) div 2 * 2;
    OO.PadBottom := StrToIntDef(edtPadBottom.Text, 0) div 2 * 2;
    OO.PadLeft := StrToIntDef(edtPadLeft.Text, 0) div 2 * 2;
    OO.PadRight := StrToIntDef(edtPadRight.Text, 0) div 2 * 2;
    OO.PadColor := clBlack;
  end;

{
  OO.AudioLanguage := 'eng';
  OO.SubTitleLanguage := 'eng';
}

  // TODO: more options

  // change vcd/svcd/dvd file ext to .mpg
  if Pos(LFileExt, '.vcd.svcd.dvd') > 0 then
  begin
    LFileExt := '_' + Copy(LFileExt, 2, MaxInt);
    if OO.NormType = ntPAL then
      LFileExt := LFileExt + '_pal.mpg'
    else if OO.NormType = ntNTSC then
      LFileExt := LFileExt + '_ntsc.mpg'
    else
      LFileExt := LFileExt + '_auto.mpg';
  end;

  // generate output filename automatically
  OO.FileExt := LFileExt;
  if edtOutPath.Text = SSameAsInput then
    LBaseName := ChangeFileExt(FAVProbe.FileName, '')
  else
    LBaseName := edtOutPath.Text + ChangeFileExt(ExtractFileName(FAVProbe.FileName), '');
  OO.FileName := LBaseName + '_(new)' + OO.FileExt;
  if FileExists(OO.FileName) and not chkOverwrite.Checked then
  begin
    I := 1;
    while FileExists(LBaseName + '_(new_' + IntToStr(I) + ')' + OO.FileExt) do
      Inc(I);
    OO.FileName := LBaseName + '_(new_' + IntToStr(I) + ')' + OO.FileExt;
  end;

  // NOTICE: the component will use OutputFileName's FileExt to guess
  //         the output format when IO.FileFormat isn't defined.

  OO.DisableSubtitle := True;
  Result := OO;
end;

end.