///////////////////////////////////////////////////////////////////////
// Copyright (C) Andru 2007                                          //
// mail: dr.andru@gmail.com                                          //
// site: http://andru.2x4.ru                                         //
//                                                                   //
// OMEGA Engine is free software.                                    //
//                                                                   //
// You may redistribute it and/or modify it under the terms of the   //
// GNU General Public License, as published by the Free Software     //
// Foundation; either version 2 of the License, or (at your option)  //
// any later version.                                                //
//                                                                   //
// this module is distributed in the hope that it will be useful,    //
// but WITHOUT ANY WARRANTY; without even the implied warranty of    //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.              //
// See the GNU General Public License for more details.              //
//                                                                   //
// You should have received a copy of the GNU General Public License //
// along with this module.  If not, write to:                        //
// 	The Free Software Foundation, Inc.,                              //
// 	51 Franklin Street, Fifth Floor                                  //
// 	Boston, MA  02110-1301, USA.                                     //
///////////////////////////////////////////////////////////////////////
unit MEDIA_DSHOW;

{$I DEFINES.pas}

interface
uses
  Windows,
  minFile,
  //DirectShow,

  ENGINE_VAR,
  ENGINE_CONST,

  ENGINE_LOG,

  ENGINE_UTILS,
  UTILS;

const
  CLSCTX_INPROC_SERVER     = 1;
  CLSID_FilterGraph: TGUID = (D1:$E436EBB3;D2:$524F;D3:$11CE;D4:($9F,$53,$00,$20,$AF,$0B,$A7,$70));

type
  PIID = PGUID;
  TIID = TGUID;
  PCLSID = PGUID;
  TCLSID = TGUID;
  HSEMAPHORE = Longint;

  IPersist = interface(IUnknown)
    ['{0000010C-0000-0000-C000-000000000046}']
    function GetClassID(out classID: TCLSID): HResult; stdcall;
  end;

  TAM_Media_Type = record
    majortype: TGUID;
    subtype: TGUID;
    bFixedSizeSamples: BOOL;
    bTemporalCompression: BOOL;
    lSampleSize: ULONG;
    formattype: TGUID;
    pUnk: IUnknown;
    cbFormat: ULONG;
    pbFormat: Pointer;
  end;
  PAM_Media_Type = ^TAM_Media_Type;

  IBaseFilter = interface;

  TPin_Direction = (
    PINDIR_INPUT,
    PINDIR_OUTPUT
  );

  TPin_Info = record
    pFilter: IBaseFilter;
    dir: TPin_Direction;
    achName: array[0..127] of WCHAR;
  end;

  IEnumFilters = interface;
  IEnumMediaTypes = interface;

  IPin = interface(IUnknown)
    ['{56A86891-0AD4-11CE-B03A-0020AF0BA770}']
    function Connect(pReceivePin: IPin; const pmt: PAM_Media_Type): HRESULT; stdcall;
    function ReceiveConnection(pConnector: IPin; const pmt: TAM_Media_Type): HRESULT; stdcall;
    function Disconnect: HRESULT; stdcall;
    function ConnectedTo(out pPin: IPin): HRESULT; stdcall;
    function ConnectionMediaType(out pmt: TAM_Media_Type): HRESULT; stdcall;
    function QueryPinInfo(out pInfo: TPin_Info): HRESULT; stdcall;
    function QueryDirection(out pPinDir: TPin_Direction): HRESULT; stdcall;
    function QueryId(out Id: LPWSTR): HRESULT; stdcall;
    function QueryAccept(const pmt: TAM_Media_Type): HRESULT; stdcall;
    function EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT; stdcall;
    function QueryInternalConnections(out apPin: IPin; var nPin: ULONG): HRESULT; stdcall;
    function EndOfStream: HRESULT; stdcall;
    function BeginFlush: HRESULT; stdcall;
    function EndFlush: HRESULT; stdcall;
    function NewSegment(tStart, tStop: int64; dRate: double): HRESULT; stdcall;
  end;

  IEnumPins = interface(IUnknown)
    ['{56A86892-0AD4-11CE-B03A-0020AF0BA770}']
    function Next(cPins: ULONG; out ppPins: IPin; pcFetched: PULONG): HRESULT; stdcall;
    function Skip(cPins: ULONG): HRESULT; stdcall;
    function Reset: HRESULT; stdcall;
    function Clone(out ppEnum: IEnumPins): HRESULT; stdcall;
  end;

  IEnumMediaTypes = interface(IUnknown)
    ['{89C31040-846B-11CE-97D3-00AA0055595A}']
    function Next(cMediaTypes: ULONG; out ppMediaTypes: PAM_Media_Type;
      pcFetched: PULONG): HRESULT; stdcall;
    function Skip(cMediaTypes: ULONG): HRESULT; stdcall;
    function Reset: HRESULT; stdcall;
    function Clone(out ppEnum: IEnumMediaTypes): HRESULT; stdcall;
  end;

  IFilterGraph = interface(IUnknown)
    ['{56A8689F-0AD4-11CE-B03A-0020AF0BA770}']
    function AddFilter(pFilter: IBaseFilter; pName: PWideChar): HRESULT; stdcall;
    function RemoveFilter(pFilter: IBaseFilter): HRESULT; stdcall;
    function EnumFilters(out ppEnum: IEnumFilters): HRESULT; stdcall;
    function FindFilterByName(pName: PWideChar; out ppFilter: IBaseFilter): HRESULT; stdcall;
    function ConnectDirect(ppinOut, ppinIn: IPin; pmt: PAM_Media_Type): HRESULT; stdcall;
    function Reconnect(ppin: IPin): HRESULT; stdcall;
    function Disconnect(ppin: IPin): HRESULT; stdcall;
    function SetDefaultSyncSource: HRESULT; stdcall;
  end;

  IEnumFilters = interface(IUnknown)
    ['{56A86893-0AD4-11CE-B03A-0020AF0BA770}']
    function Next(cFilters: ULONG; out ppFilter: IBaseFilter;
      pcFetched: PULONG): HRESULT; stdcall;
    function Skip(cFilters: ULONG): HRESULT; stdcall;
    function Reset: HRESULT; stdcall;
    function Clone(out ppEnum: IEnumFilters): HRESULT; stdcall;
  end;

  TFilter_State = (
    State_Stopped,
    State_Paused,
    State_Running
  );

  IReferenceClock = interface;

  IMediaFilter = interface(IPersist)
    ['{56A86899-0AD4-11CE-B03A-0020AF0BA770}']
    function Stop: HRESULT; stdcall;
    function Pause: HRESULT; stdcall;
    function Run(tStart: int64): HRESULT; stdcall;
    function GetState(dwMilliSecsTimeout: DWORD; out State: TFilter_State): HRESULT; stdcall;
    function SetSyncSource(pClock: IReferenceClock): HRESULT; stdcall;
    function GetSyncSource(out pClock: IReferenceClock): HRESULT; stdcall;
  end;

  TFilterInfo = record
    achName: array[0..127] of WCHAR;
    pGraph: IFilterGraph;
  end;

  IGraphBuilder = interface(IFilterGraph)
    ['{56A868A9-0AD4-11CE-B03A-0020AF0BA770}']
    function Connect(ppinOut, ppinIn: IPin): HRESULT; stdcall;
    function Render(ppinOut: IPin): HRESULT; stdcall;
    function RenderFile(lpcwstrFile, lpcwstrPlayList: PWideChar): HRESULT; stdcall;
    function AddSourceFilter(lpcwstrFileName, lpcwstrFilterName: LPCWSTR;
        out ppFilter: IBaseFilter): HRESULT; stdcall;
    function SetLogFile(hFile: THandle): HRESULT; stdcall;
    function Abort: HRESULT; stdcall;
    function ShouldOperationContinue: HRESULT; stdcall;
  end;

  IBaseFilter = interface(IMediaFilter)
    ['{56A86895-0AD4-11CE-B03A-0020AF0BA770}']
    function EnumPins(out ppEnum: IEnumPins): HRESULT; stdcall;
    function FindPin(Id: PWideChar; out ppPin: IPin): HRESULT; stdcall;
    function QueryFilterInfo(out pInfo: TFilterInfo): HRESULT; stdcall;
    function JoinFilterGraph(pGraph: IFilterGraph; pName: PWideChar): HRESULT; stdcall;
    function QueryVendorInfo(out pVendorInfo: PWideChar): HRESULT; stdcall;
  end;

  IReferenceClock = interface(IUnknown)
    ['{56A86897-0AD4-11CE-B03A-0020AF0BA770}']
    function GetTime(out pTime: int64): HRESULT; stdcall;
    function AdviseTime(baseTime, streamTime: int64;
        hEvent: THandle; out pdwAdviseCookie: DWORD): HRESULT; stdcall;
    function AdvisePeriodic(startTime, periodTime: int64;
        hSemaphore: HSEMAPHORE; out pdwAdviseCookie: DWORD): HRESULT; stdcall;
    function Unadvise(dwAdviseCookie: DWORD): HRESULT; stdcall;
  end;

  IMediaControl = interface(IDispatch)
    ['{56A868B1-0AD4-11CE-B03A-0020AF0BA770}']
    (* IMediaControl methods *)
    function Run: HResult; stdcall;
    function Pause: HResult; stdcall;
    function Stop: HResult; stdcall;
    function GetState(msTimeout: DWORD; out pfs: TFilter_State): HResult; stdcall;
    function RenderFile(strFilename: WideString): HResult; stdcall;
    function AddSourceFilter(strFilename: WideString; out ppUnk: IDispatch): HResult; stdcall;
    function get_FilterCollection(out ppUnk: IDispatch): HResult; stdcall;
    function get_RegFilterCollection(out ppUnk: IDispatch): HResult; stdcall;
    function StopWhenReady: HResult; stdcall;
  end;

  IBasicAudio = interface(IDispatch)
    ['{56A868B3-0AD4-11CE-B03A-0020AF0BA770}']
    (* IBasicAudio methods *)
    function put_Volume(lVolume: Longint): HResult; stdcall;
    function get_Volume(out plVolume: Longint): HResult; stdcall;
    function put_Balance(lBalance: Longint): HResult; stdcall;
    function get_Balance(out plBalance: Longint): HResult; stdcall;
  end;

  IMediaPosition = interface(IDispatch)
    ['{56A868B2-0AD4-11CE-B03A-0020AF0BA770}']
    (* IMediaPosition methods *)
    function get_Duration(out plength: Double): HResult; stdcall;
    function put_CurrentPosition(llTime: Double): HResult; stdcall;
    function get_CurrentPosition(out pllTime: Double): HResult; stdcall;
    function get_StopTime(out pllTime: Double): HResult; stdcall;
    function put_StopTime(llTime: Double): HResult; stdcall;
    function get_PrerollTime(out pllTime: Double): HResult; stdcall;
    function put_PrerollTime(llTime: Double): HResult; stdcall;
    function put_Rate(dRate: double): HResult; stdcall;
    function get_Rate(out pdRate: double): HResult; stdcall;
    function CanSeekForward(out pCanSeekForward: Longint): HResult; stdcall;
    function CanSeekBackward(out pCanSeekBackward: Longint): HResult; stdcall;
  end;

  IVideoWindow = interface(IDispatch)
    ['{56A868B4-0AD4-11CE-B03A-0020AF0BA770}']
    (* IVideoWindow methods *)
    function put_Caption(strCaption: WideString): HResult; stdcall;
    function get_Caption(out strCaption: WideString): HResult; stdcall;
    function put_WindowStyle(WindowStyle: Longint): HResult; stdcall;
    function get_WindowStyle(out WindowStyle: Longint): HResult; stdcall;
    function put_WindowStyleEx(WindowStyleEx: Longint): HResult; stdcall;
    function get_WindowStyleEx(out WindowStyleEx: Longint): HResult; stdcall;
    function put_AutoShow(AutoShow: LongBool): HResult; stdcall;
    function get_AutoShow(out AutoShow: LongBool): HResult; stdcall;
    function put_WindowState(WindowState: Longint): HResult; stdcall;
    function get_WindowState(out WindowState: Longint): HResult; stdcall;
    function put_BackgroundPalette(BackgroundPalette: Longint): HResult; stdcall;
    function get_BackgroundPalette(out pBackgroundPalette: Longint): HResult; stdcall;
    function put_Visible(Visible: LongBool): HResult; stdcall;
    function get_Visible(out pVisible: LongBool): HResult; stdcall;
    function put_Left(Left: Longint): HResult; stdcall;
    function get_Left(out pLeft: Longint): HResult; stdcall;
    function put_Width(Width: Longint): HResult; stdcall;
    function get_Width(out pWidth: Longint): HResult; stdcall;
    function put_Top(Top: Longint): HResult; stdcall;
    function get_Top(out pTop: Longint): HResult; stdcall;
    function put_Height(Height: Longint): HResult; stdcall;
    function get_Height(out pHeight: Longint): HResult; stdcall;
    function put_Owner(Owner: Longint): HResult; stdcall;
    function get_Owner(out Owner: Longint): HResult; stdcall;
    function put_MessageDrain(Drain: Longint): HResult; stdcall;
    function get_MessageDrain(out Drain: Longint): HResult; stdcall;
    function get_BorderColor(out Color: Longint): HResult; stdcall;
    function put_BorderColor(Color: Longint): HResult; stdcall;
    function get_FullScreenMode(out FullScreenMode: LongBool): HResult; stdcall;
    function put_FullScreenMode(FullScreenMode: LongBool): HResult; stdcall;
    function SetWindowForeground(Focus: Longint): HResult; stdcall;
    function NotifyOwnerMessage(hwnd: Longint; uMsg, wParam, lParam: Longint): HResult; stdcall;
    function SetWindowPosition(Left, Top, Width, Height: Longint): HResult; stdcall;
    function GetWindowPosition(out pLeft, pTop, pWidth, pHeight: Longint): HResult; stdcall;
    function GetMinIdealImageSize(out pWidth, pHeight: Longint): HResult; stdcall;
    function GetMaxIdealImageSize(out pWidth, pHeight: Longint): HResult; stdcall;
    function GetRestorePosition(out pLeft, pTop, pWidth, pHeight: Longint): HResult; stdcall;
    function HideCursor(HideCursor: LongBool): HResult; stdcall;
    function IsCursorHidden(out CursorHidden: LongBool): HResult; stdcall;
  end;

function CoInitialize(pvReserved: Pointer): HResult; stdcall;
procedure CoUninitialize; stdcall;
function CoCreateInstance(const clsid: TCLSID; unkOuter: IUnknown;
  dwClsContext: Longint; const iid: TIID; out pv): HResult; stdcall;

//----------------------------------------------------------------------------//
//--------------------- Ï Î Ë Ü Ç Î Â À Ò Å Ë Ü Ñ Ê È Å ----------------------//
//----------------------------------------------------------------------------//
procedure Media_Play( FileName : PChar; Loop : Boolean ); stdcall;
procedure Media_Replay; stdcall;
procedure Media_Pause; stdcall;
procedure Media_Resume; stdcall;
procedure Media_Stop; stdcall;
procedure Media_SetVolume( Volume : Integer ); stdcall;
function  Media_GetLength : Double; stdcall;
function Media_GetPosition : Double; stdcall;
procedure Media_SetPosition( Position : Double ); stdcall;
procedure Media_SetWindowRect( X, Y, Width, Height : Integer ); stdcall;

procedure Media_DeInit;
procedure Media_Loop;

var
  MediaLoop : Boolean;

//----------------------------------------------------------------------------//
                                 IMPLEMENTATION
//----------------------------------------------------------------------------//
const
  IID_IGraphBuilder  : TGUID = '{56A868A9-0AD4-11CE-B03A-0020AF0BA770}';
  IID_IMediaControl  : TGUID = (D1:$56A868B1;D2:$0AD4;D3:$11CE;D4:($B0,$3A,$00,$20,$AF,$0B,$A7,$70));
  IID_IBasicAudio    : TGUID = (D1:$56A868B3;D2:$0AD4;D3:$11CE;D4:($B0,$3A,$00,$20,$AF,$0B,$A7,$70));
  IID_IMediaPosition : TGUID = (D1:$56A868B2;D2:$0AD4;D3:$11CE;D4:($B0,$3A,$00,$20,$AF,$0B,$A7,$70));
  IID_IVideoWindow   : TGUID = (D1:$56A868B4;D2:$0AD4;D3:$11CE;D4:($B0,$3A,$00,$20,$AF,$0B,$A7,$70));
var
  GraphBuilder    : IGraphBuilder  = nil;
  MediaControl    : IMediaControl  = nil;
  AudioControl    : IBasicAudio    = nil;
  PositionControl : IMediaPosition = nil;
  VideoWindow     : IVideoWindow   = nil;

  MediaInit    : Boolean;

function CoCreateInstance;         external 'ole32.dll' name 'CoCreateInstance';
function CoInitialize;             external 'ole32.dll' name 'CoInitialize';
procedure CoUninitialize;          external 'ole32.dll' name 'CoUninitialize';

procedure Media_DeInit;
Begin
if MediaInit Then
  begin
    MediaControl.StopWhenReady;
    VideoWindow     := nil;
    PositionControl := nil;
    AudioControl    := nil;
    MediaControl    := nil;
    GraphBuilder    := nil;

    CoUninitialize;

    if app_Log Then
      LOG_AddMessage( LOGT_ENGINE, LOG_MEDIA_SUCCESSFUL2 );
  end;
End;

procedure Media_Play;
Begin

FileName := PChar( String( app_Dir ) + String( FileName ) );

if CoCreateInstance( CLSID_FilterGraph,
                     nil,
                     CLSCTX_INPROC_SERVER,
                     IID_IGraphBuilder,
                     GraphBuilder ) <> S_OK Then
  E_Error( ERR_DSHOW_INIT );
GraphBuilder.QueryInterface( IID_IMediaControl,  MediaControl );
GraphBuilder.QueryInterface( IID_IBasicAudio,    AudioControl );
GraphBuilder.QueryInterface( IID_IMediaPosition, PositionControl );
GraphBuilder.QueryInterface( IID_IVideoWindow,   VideoWindow );

if not MediaInit Then
  begin
    MediaInit := TRUE;
    if app_Log Then
      LOG_AddMessage( LOGT_ENGINE, LOG_MEDIA_SUCCESSFUL );
  end;

MediaControl.RenderFile( PWideChar( WideString( FileName ) ) );

MediaControl.Run;

MediaLoop := Loop;

if not FileExists( FileName ) Then
  E_Error( ERR_CANT_FIND_FILE + FileName );

End;

procedure Media_Replay;
Begin
MediaControl.Stop;
PositionControl.put_CurrentPosition( 0 );
MediaControl.Run;
End;

procedure Media_Pause;
Begin
MediaControl.Pause;
End;

procedure Media_Resume;
Begin
MediaControl.Run;
End;

procedure Media_Stop;
Begin
PositionControl.put_CurrentPosition( Media_GetLength + 1 );
MediaLoop := FALSE;
MediaControl.Stop;
End;

procedure Media_Loop;
  var
    Pos, Len : Double;
Begin
PositionControl.get_StopTime( Len );
PositionControl.get_CurrentPosition( Pos );
if Pos >= Len Then
  Media_Replay;
End;

procedure Media_SetVolume;
Begin
AudioControl.put_Volume( Volume )
End;

function Media_GetLength;
Begin
PositionControl.get_StopTime( Result );
End;

function Media_GetPosition;
Begin
PositionControl.get_CurrentPosition( Result );
End;

procedure Media_SetPosition;
Begin
PositionControl.put_CurrentPosition( Position );
End;

procedure Media_SetWindowRect;
Begin
VideoWindow.put_Owner( wnd_HWND );
VideoWindow.put_WindowStyle( WS_CHILD or WS_CLIPSIBLINGS );

VideoWindow.SetWindowPosition( X, Y, Width, Height );

VideoWindow.put_AutoShow( TRUE );
End;

initialization
  CoInitialize( nil );
finalization
  CoUninitialize;

end.
