// The Options are almost compatible with the parameters of ffmpeg command line tool.
// Please refer to https://ffmpeg.org/ffmpeg.html for detail.

type
  PEventOptions = ^TEventOptions;
  TEventOptions = record
    AudioInputHook: Boolean;  // whether enable OnAudioInputHook event
    AudioOutputHook: Boolean; // whether enable OnAudioOutputHook event
    VideoInputHook: Boolean;  // whether enable OnVideoInputHook event
    VideoOutputHook: Boolean; // whether enable OnVideoOutputHook event
    FrameInputHook: Boolean;  // whether enable OnFrameInputHook event
    FrameOutputHook: Boolean; // whether enable OnFrameOutputHook event
  end;

  // NOTICE: TBaseFFmpeg in unit FFmpegHeader.pas
  TBaseFFmpeg = class
  public
    constructor Create; virtual;
    destructor Destroy; override;

    property LastErrMsg: string read FLastErrMsg;
    property OutputDuration: Int64 read FOutputDuration;
    // specifies the number of adjacent color bits on each plane needed to define a pixel.
    // one of (8, 15[555, BI_RGB], 16[565, BI_BITFIELDS], 24, 32), default to 32
    property VideoHookBitsPixel: Integer read GetVideoHookBitsPixel write SetVideoHookBitsPixel;
  end;

  // NOTICE: TCustomFFmpeg in unit FFmpeg.pas
  TCustomFFmpeg = class(TBaseFFmpeg)
  public
    constructor Create; override;
    destructor Destroy; override;

    procedure StartConvert(const AUseThread: Boolean);
    procedure Stop(const AWaitForStop: Boolean);
    procedure Pause;
    procedure Resume;
    procedure Step;

    property Converting: Boolean read FConverting;
{$IFDEF VCL_XE_OR_ABOVE}
    property ThreadID: TThreadID read FThreadID;
{$ELSE}
    property ThreadID: LongWord read FThreadID;
{$ENDIF}
    property Finished: Boolean read FFinished;
    property FatalException: Boolean read FFatalException;
    property FatalMessage: string read FFatalMessage;
    property Terminated: Boolean read FTerminated;
    property OnInputAudioHook: TFFAudioHookEvent read FOnInputAudioHook write FOnInputAudioHook;
    property OnOutputAudioHook: TFFAudioHookEvent read FOnOutputAudioHook write FOnOutputAudioHook;
    property OnInputVideoHook: TFFVideoHookEvent read FOnInputVideoHook write FOnInputVideoHook;
    property OnOutputVideoHook: TFFVideoHookEvent read FOnOutputVideoHook write FOnOutputVideoHook;
    property OnInputFrameHook: TFFFrameHookEvent read FOnInputFrameHook write FOnInputFrameHook;
    property OnOutputFrameHook: TFFFrameHookEvent read FOnOutputFrameHook write FOnOutputFrameHook;
  end;

  TFFmpeg = class(TCustomFFmpeg)
  public
    constructor Create; override;
    destructor Destroy; override;
    property CmdOptions: string read FCmdOptions;
    property Parent: TFFBaseComponent read FParent;
  end;

  // PCR - Program Clock Reference
  // STC - System Time Clock
  // SCR - System Clock Reference
  // PTS - Presentation Time Stamp
  // DTS - Decode Time Stamp

{$IFNDEF BCB}
  PAVFrame  = libavutil_frame.PAVFrame;
  TAVSampleFormat = libavutil_samplefmt.TAVSampleFormat;
{$ENDIF}

  TEncAudioHookEvent = procedure(Sender: TObject; ATaskIndex: Integer;
    const APTS: Int64; AFrame: PAVFrame;
    ASize, ASampleRate, AChannels: Integer; ASampleFormat: TAVSampleFormat) of object;

  PHookInfo = ^THookInfo;
  THookInfo = record
    TaskIndex: Integer;     // index of converting tasks
    Bitmap: TBitmap;        // bitmap filled with the original video picture, you can save it,
                            //  or change it by drawing text or image on bitmap.Canvas,
                            //  but you must NOT change the size and the PixelFormat of the bitmap!
    FrameNumber: Integer;   // frame index number, first is 1 not 0
    PTS: Int64;             // presentation time stamp of current picture, in microseconds
    Update: Boolean;        // whether update the bitmap back to original video picture, default True
    StopHook: Boolean;      // whether stop video hook, if true then VideoHook event will not
                            //  be triggered with this file, default False
  end;
  // only triggered with OutputOptions.VideoInputHook/VideoOutputHook = True
  TEncVideoHookEvent = procedure(Sender: TObject; AHookInfo: PHookInfo) of object;

  PFrameHookInfo = ^TFrameHookInfo;
  TFrameHookInfo = record
    TaskIndex: Integer;     // index of converting tasks
    FrameNumber: Integer;   // frame index number, first is 1 not 0
    Picture: PAVPicture;
    PixFmt: TAVPixelFormat;
    Width: Integer;
    Height: Integer;
    PTS: Int64;             // presentation time stamp of current picture, in microseconds
  end;
  // only triggered with OutputOptions.FrameInputHook/FrameOutputHook = True
  TEncFrameHookEvent = procedure(Sender: TObject; AHookInfo: PFrameHookInfo) of object;

  TPreviewInfo = record
    TaskIndex: Integer;     // index of converting tasks
    Bitmap: TBitmap;        // bitmap filled with the target video picture, you can save it,
                            //  or paint it on any canvas(or any other control) which you want.
    FrameNumber: Integer;   // frame index number, first is 1 not 0
    PTS: Int64;             // presentation time stamp of current picture, in microseconds
  end;
  // only triggered with property PreviewBitmap = True
  TEncPreviewBitmapEvent = procedure(Sender: TObject; const APreviewInfo: TPreviewInfo) of object;

  PProgressInfo = ^TProgressInfo;
  TProgressInfo = record
    TaskIndex: Integer;     // index of converting tasks
    FileIndex: Integer;     // index of input files in the current task
    FrameNumber: Integer;   // current frame number
    FPS: Integer;           // video converting speed, frames per second, not valid when only audio output
    Quality: Single;        // quality
    BitRate: Single;        // bitrate
    CurrentSize: Int64;     // current output file size in bytes
    CurrentDuration: Int64; // current duration time in microsecond
    TotalDuration: Int64;   // total output duration time in microsecond
  end;
  TEncProgressEvent = procedure(Sender: TObject; AProgressInfo: PProgressInfo) of object;

  TStartEvent = procedure(Sender: TObject; ATaskIndex: Integer) of object;

  TTerminateInfo = record
    TaskIndex: Integer;     // index of converting tasks, (-1) means all tasks are terminated
    Finished: Boolean;      // True means converted success, False means converting broken
    Exception: Boolean;     // True means Exception occured, False please ignore
    ExceptionMsg: string;   // Exception message
  end;
  TTerminateEvent = procedure(Sender: TObject; const ATerminateInfo: TTerminateInfo) of object;

  TCustomEncoder = class(TFFBaseComponent)
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure SetLicenseKey(AKey: AnsiString);

    function AVLibLoaded: Boolean;
    function LoadAVLib(const APath: TPathFileName): Boolean;
    procedure UnloadAVLib;

    function AddTask(const AOptions: string; const AEventOptions: PEventOptions = nil): Integer;

    function SendFilterCommand(ATaskIndex: Integer; target, cmd, arg: string; flags: Integer): Boolean;
    function QueueFilterCommand(ATaskIndex: Integer; target, cmd, arg: string; flags: Integer; pts: Double): Boolean;

    procedure ClearTasks;
    procedure Exchange(ATaskIndex1, ATaskIndex2: Integer);
    procedure RemoveTask(ATaskIndex: Integer);
    function GetTaskIndex(AFFmpeg: TFFmpeg): Integer;

    procedure Start(ATaskCount: Integer = 0);
    procedure Pause;
    procedure Resume;
    procedure Step;
    procedure Stop(AWaitForStop: Boolean = True);

    property Decoders[TaskIndex, FileIndex: Integer]: TFFDecoder read GetDecoders;
    property FFmpegs[TaskIndex: Integer]: TFFmpeg read GetFFmpegs;

    property InputCount[TaskIndex: Integer]: Integer read GetInputCount;
    property InputFiles[TaskIndex, FileIndex: Integer]: PAVFormatContext read GetInputFiles;
    property InputFilenames[TaskIndex, FileIndex: Integer]: string read GetInputFilenames;
    property OutputCount[TaskIndex: Integer]: Integer read GetOutputCount;
    property OutputFiles[TaskIndex, FileIndex: Integer]: PAVFormatContext read GetOutputFiles;
    property OutputFilenames[TaskIndex, FileIndex: Integer]: string read GetOutputFilenames;
    property LastErrMsg: string read FLastErrMsg;
    property TasksCount: Integer read GetTasksCount;

    property AutoReleaseTask: Boolean read FAutoReleaseTask write FAutoReleaseTask default True;
    property PreviewAudio: Boolean read FPreviewAudio write SetPreviewAudio default False;
    property PreviewVideo: Boolean read FPreviewVideo write SetPreviewVideo default False;
    property PreviewBitmap: Boolean read FPreviewBitmap write SetPreviewBitmap default False;
    property PreviewInterval: Integer read FPreviewInterval write SetPreviewInterval default CPreviewInterval;
    property ProgressInterval: Integer read FProgressInterval write SetProgressInterval default CReportInterval;
    property FrameRateEmulation: Boolean read GetFrameRateEmulation write SetFrameRateEmulation;
    property Terminated: Boolean read FTerminated;
    property TriggerEventInMainThread: Boolean read FTriggerEventInMainThread write FTriggerEventInMainThread default True;
    property UserData: Pointer read FData write FData;
    property Working: Boolean read FWorking;
    property Paused: Boolean read FPaused;
    property Stepped: Boolean read FStepped;

    property OnAudioInputHook: TEncAudioHookEvent read FOnInputAudioHook write FOnInputAudioHook;
    property OnAudioOutputHook: TEncAudioHookEvent read FOnOutputAudioHook write FOnOutputAudioHook;
    property OnBeforeFindStreamInfo: TBeforeFindStreamInfoEvent read FOnBeforeFindStreamInfo write FOnBeforeFindStreamInfo;
    property OnPreviewBitmap: TEncPreviewBitmapEvent read FOnPreviewBitmap write FOnPreviewBitmap;
    property OnProgress: TEncProgressEvent read FOnProgress write FOnProgress;
    property OnStart: TStartEvent read FOnStart write FOnStart;
    property OnTerminate: TTerminateEvent read FOnTerminate write FOnTerminate;
    property OnVideoInputHook: TEncVideoHookEvent read FOnInputVideoHook write FOnInputVideoHook;
    property OnVideoOutputHook: TEncVideoHookEvent read FOnOutputVideoHook write FOnOutputVideoHook;
    property OnFrameInputHook: TEncFrameHookEvent read FOnInputFrameHook write FOnInputFrameHook;
    property OnFrameOutputHook: TEncFrameHookEvent read FOnOutputFrameHook write FOnOutputFrameHook;
  end;

  TFFEncoder = class(TCustomEncoder)
  published
    property PreviewAudio;
    property PreviewVideo;
    property PreviewBitmap;
    property PreviewInterval;
    property ProgressInterval;
    property TriggerEventInMainThread;

    property OnAudioInputHook;
    property OnAudioOutputHook;
    property OnBeforeFindStreamInfo;
    property OnPreviewBitmap;
    property OnProgress;
    property OnStart;
    property OnTerminate;
    property OnVideoInputHook;
    property OnVideoOutputHook;
    property OnFrameInputHook;
    property OnFrameOutputHook;
  end;

procedure InitEventOptions(P: PEventOptions);

implementation

procedure InitEventOptions(P: PEventOptions);
begin
  with P^ do
  begin
    AudioInputHook := False;
    AudioOutputHook := False;
    VideoInputHook := False;
    VideoOutputHook := False;
    FrameInputHook := False;
    FrameOutputHook := False;
  end;
end;
