type
  // enum types
  TShowMode = (smDefault, smVideo, smWaves, smRDFT, smNone);

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

    procedure SetLicenseKey(const AKey: AnsiString);

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

    function SDLWasInit: Boolean;
    function InitSDL: Boolean; // It will be called in Open()/TryOpen()
    procedure QuitSDL;

    procedure Stop(AWaitForStop: Boolean = False);
    procedure Pause;
    procedure Resume;
    procedure TogglePause;
    procedure ToggleAudioDisplay;
    procedure StepToNextFrame;
    procedure FlushQueue;

    function CurrentFrame: TBitmap; overload;
    function CurrentFrame(out APTS: Int64): TBitmap; overload;
    function CurrentFrame(var ABitmap: TBitmap): Boolean; overload;
    // Send a command to one or more filter instances.
    function SendVideoFilterCommand(target, cmd, arg: string; flags: Integer): Boolean;
    function SendAudioFilterCommand(target, cmd, arg: string; flags: Integer): Boolean;
    // Queue a command to one or more filter instances.
    function QueueVideoFilterCommand(target, cmd, arg: string; flags: Integer; pts: Double): Boolean;
    function QueueAudioFilterCommand(target, cmd, arg: string; flags: Integer; pts: Double): Boolean;
    // do seek by timestamp in microseconds
    procedure Seek(const APTS: Int64; ASeekFlags: TSeekFlags = []; AWaitForSeekEnd: Boolean = False); overload;
    // do seek by relative value (backward/forward) in seconds
    procedure Seek(const ADelta: Double; ASeekFlags: TSeekFlags = []; AWaitForSeekEnd: Boolean = False); overload;

    // frame width
    property FrameWidth: Integer read FFrameWidth;
    // frame height
    property FrameHeight: Integer read FFrameHeight;

    property AVFormatContext: PAVFormatContext read FAVFormatContext;
    property FileName: TPathFileName read Finput_filename;
    property LastErrMsg: string read FLastErrMsg;

    property AudioStreamIndex: Integer read GetAudioStream write SetAudioStream;
    property VideoStreamIndex: Integer read GetVideoStream write SetVideoStream;
    property SubtitleStreamIndex: Integer read GetSubtitleStream write SetSubtitleStream;

    property Decoder: TFFDecoder read FDecoder;

    // get all audio drivers after SDL2 loaded, SDL2 will be loaded in LoadAVLib() function
    property AudioDrivers: TStringList read GetAudioDrivers;
    // set audio driver before InitSDL(), or leave it empty for default
    // get current audio driver after InitSDL()
    property AudioDriver: string read GetAudioDriver write FAudioDriver;
    // get all audio devices after InitSDL()
    property AudioDevices: TStringList read GetAudioDevices;
    // set audio device before Open()/TryOpen(), or leave it empty for default
    property AudioDevice: string read FGetAudioDevice write FSetAudioDevice;
    // enable/disable audio hook event
    property AudioHook: Boolean read FAudioHook write FAudioHook;
    // AudioVolume: 0-128, 128 means keeping original
    property AudioVolume: TAudioVolume read FAudioVolume write FAudioVolume;
    // background color, value of RGB(r, g, b)
    property BackColor: Cardinal read GetBackColor write SetBackColor;
    // audio line color, value of RGB(r, g, b)
    property LineColor: Cardinal read GetLineColor write SetLineColor;
    // audio wave color, value of RGB(r, g, b)
    property WaveColor: Cardinal read GetWaveColor write SetWaveColor;
    property Mute: Boolean read FMute write FMute;
    property CurrentAVFrame: TAVFrame read GetCurrentAVFrame;
    property HookingAVFrame: PAVFrame read FHookingAVFrame;
    property CurrentPTS: Int64 read FCurrentPTS;
    property CurrentPos: Int64 read FCurrentPos;
    property CheckIOEOF: Boolean read FCheckIOEOF write FCheckIOEOF;
    property CheckIOERR: Boolean read FCheckIOERR write FCheckIOERR;
    property CheckReadPause: Boolean read FCheckReadPause write FCheckReadPause;
    property NegativeOneAsEOF: Boolean read FNegativeOneAsEOF write FNegativeOneAsEOF;
    property ResetFilterAfterSeeking: Boolean read FResetFilterAfterSeeking write FResetFilterAfterSeeking;
    property FrameHook: Boolean read FFrameHook write FFrameHook;
    property OpenInCallerThread: Boolean read FOpenInCallerThread write FOpenInCallerThread;
    property Paused: Boolean read GetPaused;
    property PlayState: TPlayState read FState;
    property PlayTime: Int64 read FPlayTime write FPlayTime;
    // size of audio queue plus video queue
    property QueueSize: Integer read GetQueueSize;
    // size of audio queue
    property AudioQueueSize: Integer read GetAudioQueueSize;
    // size of video queue
    property VideoQueueSize: Integer read GetVideoQueueSize;
    // size of subtitle queue
    property SubtitleQueueuSize: Integer read GetSubtitleQueueSize;
    // property AudioPitch requires SoundTouch library
    // Sets new pitch control value. Original pitch = 1.0, smaller values
    // represent lower pitches, larger values higher pitch.
    // AudioPitch := Exp(Ln(2) * octaves); // example for changing pitch in octaves
    // AudioPitch := Exp(Ln(2) * semitones / 12); // example for changing pitch in semitones
    property AudioPitch: Single read FAudioPitch write SetAudioPitch;
    // playback speed, requires SyncType to be stExternal
    property PlaybackSpeed: Double read FPlaybackSpeed write SetPlaybackSpeed;
    // set audio-video sync. type (type=audio/video/ext)
    property SyncType: Tav_sync_type read Fav_sync_type write Setav_sync_type;
    // use SoundTouch library to change audio speed, otherwise use FFmpeg libraries
    property UseSoundTouch: Boolean read FUseSoundTouch write FUseSoundTouch;
    // repeat type on reach of PlayTime
    property RepeatType: TRepeatType read FRepeatType write FRepeatType;
    property SDLAllowedChangesFlag: Integer read FSDLAllowedChangesFlag write FSDLAllowedChangesFlag;
    property Seeking: Boolean read FSeeking;
    // seek to a given position in microseconds
    property StartTime: Int64 read Fstart_time write SetStartTime;
    property StepToKeyFrame: Boolean read FStepToKeyFrame write SetStepToKeyFrame;
    property TriggerEventInMainThread: Boolean read FTriggerEventInMainThread
      write FTriggerEventInMainThread;
    property UseAudioPosition: Boolean read FUseAudioPosition write FUseAudioPosition;
{$IFDEF MSWINDOWS}
    property UseWindowsMessages: Boolean read FUseWindowsMessages write FUseWindowsMessages;
{$ENDIF}
    property PositionInterval: Int64 read FPositionInterval write FPositionInterval;
    property VerticalFlip: Boolean read FVerticalFlip write SetVerticalFlip;
    // video filters
    property VideoFilters: string read Fvfilters write Set_vfilters;
    // audio filters
    property AudioFilters: string read Fafilters write Set_afilters;
    // force full screen
    property is_full_screen: Integer read Fis_full_screen write Fis_full_screen;
    // seek by bytes 0=off 1=on -1=auto
    property seek_by_bytes: Integer read Fseek_by_bytes write Fseek_by_bytes;
    // seek to a given position in microseconds (before open)
    property start_time: Int64 read Fstart_time write Fstart_time;
    // drop frames when cpu is too slow
    property framedrop: Integer read Fframedrop write Fframedrop;
    // set audio-video sync. type (type=audio/video/ext)
    property av_sync_type: Tav_sync_type read Fav_sync_type write Setav_sync_type;
  published
    property OnAudioHook: TAudioHookEvent read FOnAudioHook write FOnAudioHook;
    property OnBeforeFindStreamInfo: TBeforeFindStreamInfoEvent read FOnBeforeFindStreamInfo write FOnBeforeFindStreamInfo;
    property OnError: TErrorNotifyEvent read FOnError write FOnError;
    property OnFileOpen: TFileOpenEvent read FOnFileOpen write FOnFileOpen;
    property OnFrameHook: TFrameHookEvent read FOnFrameHook write FOnFrameHook;
    property OnOpenFailed: TNotifyEvent read FOnOpenFailed write FOnOpenFailed;
    property OnPosition: TPositionEvent read FOnPosition write FOnPosition;
    property OnPosition2: TPosition2Event read FOnPosition2 write FOnPosition2;
    property OnState: TPlayStateEvent read FOnPlayState write FOnPlayState;
  end;

{$IFDEF MSWINDOWS}
// return desktop handle
function GetDesktopHandle: {$IFDEF BCB}Pointer{$ELSE}HWND{$ENDIF};
// initialize multi-threaded mode for COM, otherwise XAudio2Create() will fail
//   when try to open audio device by "xaudio2" audio driver in thread
// SDL2 2.0.6 The new default audio driver on Windows is WASAPI.
//            The old XAudio2 audio driver is deprecated.
// SDL2 2.0.7 XAudio2 is removed.
procedure InitMultiThread;
procedure UninitMultiThread;
{$ENDIF}

implementation

{$IFDEF MSWINDOWS}
// return desktop handle
function enumUserWindowsCB(ahwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
  wflags: NativeInt;
  sndWnd: HWND;
  targetWnd: HWND;
  resultHwnd: PNativeInt;
begin
  wflags := GetWindowLong(ahwnd, GWL_STYLE);
  if (wflags and WS_VISIBLE) <> 0 then
  begin
    sndWnd := FindWindowEx(ahwnd, 0, 'SHELLDLL_DefView', nil);
    if sndWnd <> 0 then
    begin
      targetWnd := FindWindowEx(sndWnd, 0, 'SysListView32', 'FolderView');
      if targetWnd <> 0 then
      begin
        resultHwnd := PNativeInt(lParam);
        resultHwnd^ := targetWnd;
        Result := False;
        Exit;
      end;
    end;
  end;
  Result := True;
end;
function GetDesktopHandle: {$IFDEF BCB}Pointer{$ELSE}HWND{$ENDIF};
var
  H: HWND;
begin
  // works under Windows XP or classic theme under Windows Vista/7
  H := FindWindow('ProgMan', nil);    {Do not Localize}
  if H <> 0 then
  begin
    H := GetWindow(H, GW_CHILD);
    if H <> 0 then
    begin
      H := GetWindow(H, GW_CHILD);
      if H <> 0 then
      begin
        Result := {$IFDEF BCB}Pointer(H){$ELSE}H{$ENDIF};
        Exit;
      end;
    end;
  end;
  // works under Vista/7
  EnumWindows(@enumUserWindowsCB, LPARAM(@Result));
end;
{$ENDIF}

