{******************************************************************************}
{ FileName............: DvbDirectShow                                          }
{ Project.............: DVB-S                                                  }
{ Author(s)...........: MM                                                     }
{ Version.............: 2.01                                                   }
{------------------------------------------------------------------------------}
{  DirectShow interface                                                        }
{                                                                              }
{  Copyright (C) 2003-2006  M.Majoor                                           }
{                                                                              }
{  This program is free software; you can 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 program 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 program; if not, write to the Free Software                 }
{  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. }
{                                                                              }
{------------------------------------------------------------------------------}
{ Graph is also added to the Running Object Table so it can be connected to    }
{ when running (typically for debugging purposes)                              }
{                                                                              }
{------------------------------------------------------------------------------}
{ Version   Date   Comment                                                     }
{  1.00   20031113 - Initial release                                           }
{  1.01   20040213 - Added 'NoRender' parameter                                }
{  1.02   20040218 - Cleanup: might have introduced spurious runtime error at  }
{                    application ending                                        }
{                  - External access to graph (AddToRot) made variable         }
{                  - LogFile handle reset prior to destruction                 }
{  1.05   20040320 - Added <MajorDvbPsi> DirectShow filter support             }
{                    (V2.01/V2.02)                                             }
{                  - All <DvbFilter> calls are through this unit               }
{                  - Added windowless mode                                     }
{                  - If no .GRF file given the graph is created manually       }
{  1.06   20040605 - Additional Try/Except added                               }
{  1.07   20040815 - ServiceId in <..GetProgramInfo> added                     }
{                  - Added <DirectShowResize>                                  }
{  1.08   20040828 - Added CAT callback                                        }
{                  - Added PAT callback                                        }
{                  - Removed support for PsiFilter (too much differences)      }
{  1.09   20040920 - VMR9 instead of windowless mode plus some additional VRM  }
{                    stuff (eg. 'OSD')                                         }
{  1.10   20041003 - Retrieval if used filters added                           }
{                  - Added Property page detection/showing                     }
{  1.11   20041021 - Switchable method (for debugging mainly)                  }
{                    Complete rebuild                                          }
{  1.12   20041212 - More object based so multiple instances are possible      }
{                  - Auto registration of usrc.ax                              }
{  1.13   20041225 - Added AC3 pin on MPEG2 demultiplexer plus options         }
{  1.14   20050808 - Added GetCurrentImage support for getting a picture dump  }
{                    IBasicVideo used instead of IBasicVideo2                  }
{                  - E_FALSE replaced by E_FAIL so it is recognized as error   }
{  1.15   20050815 - Function added SDT callback                               }
{  2.00   20050825 - Adapted for revised DvbFilter                             }
{  2.01   20051227 - Added <DirectShowResizeRectangle>                         }
{                  - Added <DirectShowSourceCrop>                              }
{                  - Added <DirectShowDestinationCrop>                         }
{                  - Added <DirectShowSaveGraph>                               }
{                  - Options concerning different 'builders' removed           }
{                  - Log option removed                                        }
{                  - Added StreamBufferSink/Source for timeshifting functions  }
{                    NOT WORKING AND HAS BEEN DISABLED - NEED MORE TESTING     }
{                  - Added option for setting 'FormatType' for audio/video pins}
{                    MPEG2 demultiplexer, so it is accepted by the             }
{                    StreamBufferSink filter (needed for video!)               }
{                  - More QueryInterfaces replaced by 'XXXX as YYYY'           }
{                  - Multiple devices support (PIP)                            }
{                    NOT WORKING AND HAS BEEN DISABLED - NEED MORE TESTING     }
{                  - Universal source removed, Major Push Source added         }
{******************************************************************************}
unit DvbDirectShow;

interface
uses
  ActiveX,
  Classes,
  Controls,
  DirectShow9,
  //  DxErr9,
  ExtCtrls,
  Graphics,
  SysUtils,
  Windows;

const
  // Note that not all combinations are possible (eg. VRM with CaptureGraph)
  // Miscellaneous
  CDirectShowMethodSpecials                 = $FF000000;   // Special options
  CDirectShowMethodDoNotRender              = $80000000;   // Do not render graph
  CDirectShowMethodDoNotAddToRot            = $40000000;   // Do not add to ROT
  CDirectShowMethodRenderAllPins            = $20000000;   // Render all pins
  CDirectShowMethodRenderAllAudioVideo      = $10000000;   // Render all audio/video pins
  CDirectShowMethodNativeVideoSize          = $01000000;   // Keep native video size
  CDirectShowMethodRenderEx                 = $04000000;   // Use RenderEx first
  CDirectShowMethodAlternative              = $08000000;
  CDirectShowMethodNoAC3Pin                 = $00100000;   // Do not create AC3 specific pin
                                                           // Use alternative (= older) DirectShow methods
  CDirectShowMethodSetFormat                = $00200000;   // sets formattype of manually added video/audio pins

  // VMR
  CDirectShowMethodVmr                      = $0000F000;   // VMR options
  CDirectShowMethodVideoRenderer            = $00000000;   // Do not use VMR
  CDirectShowMethodVideoRenderer7           = $00007000;   // Use VMR7
  CDirectShowMethodVideoRenderer9           = $00009000;   // Use VMR9
  // VMR specials
  CDirectShowMethodVmrSpecials              = $00000F00;   // VMR special options
  CDirectShowMethodVideoRendererWindowless  = $00000100;   // Windowless mode
  CDirectShowMethodVideoRendererFlipX       = $00000200;   // Swap/Flip/Mirror X
  CDirectShowMethodVideoRendererFlipY       = $00000400;   // Swap/Flip/Mirror Y

  IID_MajorPushSource                            = '{6D616A6F-7269-7479-6E6C-535243020100}';
  T_IID_MajorPushSource                  : TGUID = IID_MajorPushSource;
  CLSID_MajorPushSource                  : TGUID = '{6D616A6F-7269-7479-6E6C-535243000100}';
  CLSID_MajorPushSourcePropertyPage      : TGUID = '{6D616A6F-7269-7479-6E6C-535243010100}';
  CLSID_MajorPushSourcePropertyPageAbout : TGUID = '{6D616A6F-7269-7479-6E6C-535243FF0100}';


type
  {------------------------------------------------------------------------------
    Descript: Major Push Source filter interface
   ------------------------------------------------------------------------------}
  IMajorPushSource = interface(IUnknown)
  [IID_MajorPushSource]
    function  GetVersionInformation(out Info: PChar): HRESULT; stdcall;        // Get version information
    function  GetInformation       (out Info: PChar): HRESULT; stdcall;        // Get next text information (debug)
    function  GetInformationCount  (out Count: Integer): HRESULT; stdcall;     // Get information count (debug)
    function  GetDeliveredCount    (out Count: Integer): HRESULT; stdcall;     // Get number of bytes delivered to output pin
    procedure SetLog               (OnOff: Boolean); stdcall;                  // (De)activate file log
    function  GetLog               (out OnOff: Boolean): HRESULT; stdcall;     // Get status activation log file
    procedure SetAddTimestamps     (OnOff: Boolean); stdcall;                  // (De)activate timestamps in media samples
    function  GetAddTimestamps     (out OnOff: Boolean): HRESULT; stdcall;     // Get status timestamps
    procedure SetMediaType         (MajorType: PCLSID; MinorType: PCLSID; FormatType: PCLSID; CheckType: Boolean); stdcall;                // Set media types when pin (re)connects
    function  GetMediaType         (MajorType: PCLSID; MinorType: PCLSID; FormatType: PCLSID; out CheckType: Boolean): HRESULT; stdcall;   // Get media types to use when pin (re)connects
    procedure SetBufferSize        (BufferSize: Integer; BufferAlignment: Integer); stdcall;                           // Set (preferred) buffer requirements
    function  GetBufferSize        (out BufferSize: Integer; out BufferAlignment: Integer): HRESULT; stdcall;          // Get (decided) buffer requirements
    function  GetPushedData        (Buffer: PByte; Size: Integer): HRESULT; stdcall;                                   // Get first xx bytes of media sample of last pushed data (debug)
    procedure FlushData; stdcall;                                              // Flush data (assures next data is realigned)
    function  PushData             (Buffer: PByte; Size: Integer; out Delivered: Integer): HRESULT; stdcall;           // Push data
  end;


  TSourceDemuxFilter = record
    FFilterMPEG2Demultiplexer: IBaseFilter;
    FFilterPushSource        : IBaseFilter;
    FFilterPushSourceI       : IMajorPushSource;
    FVideoPin                : IPin;
    FAudioPin                : IPin;
    FAudioAc3Pin             : IPin;
    FActiveAudioPid          : Integer;
    FActiveVideoPid          : Integer;
    FActiveAudioAc3          : Boolean;
  end;

  {------------------------------------------------------------------------------
    Descript: The DirectShow object
   ------------------------------------------------------------------------------}
  TDirectShowGraph = class(TObject)
    FOwner               : HWND;                 // Owner of 'video'
    FLogFile             : THandle;              // Log file
    FOptions             : Dword;                // Options
    FFilterGraphStreamIn : IFilterGraph2;        // Filter graph manager (stream buffer source, all other filters)
    FFilterVmr           : IBaseFilter;          // VMR filter
    FAlphaBitmap7        : VMRAlphaBitmap;
    FAlphaBitmap9        : VMR9AlphaBitmap;
    FVideoWindow         : IVideoWindow;         // Video window (non VMR mode)
    FBasicVideo          : IBasicVideo;          // Used for retrieval of (required) window size
    FWindowlessControl   : IUnknown;
    FSourceDemuxFilters  : array[0..9] of TSourceDemuxFilter;
    FSourceDemuxDefault  : Integer;              // Default FSourceDemuxFilters to use
    FRotEntry            : array[0..1] of LongInt;
    constructor Create(Owner: HWND);
    destructor Destroy; override;
  private
    { Private declarations }
    function  BlendApplicationImage(Bitmap: HDC; Src: TRect; Blend: Single; ColorKey: COLORREF): Boolean;
    function  DirectShowCheck(ResultCode: HRESULT; var ResultMessage: string): Boolean;
    function  AddToRot(UnknownGraph: IUnknown; var RotEntry: LongInt): HRESULT;
    procedure RemoveFromRot(RotEntry: LongInt);
    function  LoadGraphFile(var FilterGraph: IFilterGraph2; GraphFile: AnsiString): Boolean;
    function  SaveGraphFile(FilterGraph: IFilterGraph2; GraphFile: AnsiString): Boolean;
    procedure DirectShowAssign(Owner: HWND);
  public
    { Public declarations }
    function  DirectShowStart(Owner: HWND; GraphicsFile: string; Options: Dword; Devices: Integer; var ErrorMessage: string): Boolean;
    function  DirectShowSaveGraph(GraphFile: AnsiString): Boolean;
    procedure DirectShowStop;
    procedure DirectShowFullScreenMode(FullScreen: Boolean);
    procedure DirectShowSetVideo(Video: HWND);
    procedure DirectShowResize;
    procedure DirectShowResizeRectangle(Destination: TRect);
    procedure DirectShowSourceCrop(Crop: TRect);
    procedure DirectShowDestinationCrop(Crop: TRect);
    procedure DirectShowGetSize(var Width: Integer; var Height: Integer);
    function  DirectShowGetImage: HBITMAP;
    function  DirectShowBlendImage(Bitmap: HDC; Src: TRect; Blend: Single; ColorKey: COLORREF): Boolean;
    function  DirectShowGetFilterName(Index: Integer): string;
    function  DirectShowWindowless: Boolean;
    function  DirectShowShowPropertyPage(Parent: THandle; FilterName: WideString; QueryOnly: Boolean): Boolean;
    procedure DirectShowMpeg2DemultiplexerSetNewPids(Device: Integer; VideoPid: Integer; AudioPid: Integer; AudioAc3: Boolean);
    procedure DirectShowPushSourceSendData(Device: Integer; Buffer: PByte; BufferLength: Integer);
  end;

  {------------------------------------------------------------------------------
    Descript: Helper functions
   ------------------------------------------------------------------------------}
  function GetDibHeaderSize(Dib: PBitmapInfo): Integer;
  function GetDibDataSize(Dib: PBitmapInfo): Integer;
  function PackedDibToDibSection(Dib: PBitmapInfo): HBITMAP;

var
  DirectShowGraph: TDirectShowGraph;             // The global created DirectShow object

implementation
uses
  Dialogs,
  Forms,
  ShellApi;


const
  {------------------------------------------------------------------------------
    Descript: DirectShow identifiers
   ------------------------------------------------------------------------------}
  IID_IPersistStream  : TGUID = '{00000109-0000-0000-C000-000000000046}';
  CLSID_MPEG2Demultiplexer : TGUID = (D1:$AFB6C280; D2:$2C41; D3:$11D3; D4:($8A, $60, $00, $00, $F8, $1E, $0E, $4A));
  IID_ISpecifyPropertyPages: TGUID = '{B196B28B-BAB4-101A-B69C-00AA00341D07}'; // See DsUtil

  {------------------------------------------------------------------------------
    Params  : <ResultCode>     Result code to check
    Returns : <Result>         True if no error
              <ResultMessage>  Error message

    Descript: Check result code and generate an error message if in error.
    Notes   :
   ------------------------------------------------------------------------------}

function TDirectShowGraph.DirectShowCheck(ResultCode: HRESULT; var ResultMessage: string): Boolean;
var
  //  RString     : PChar;
  //  RDescription: PChar;
  RError: string;
  Size  : Dword;
begin
  Result := Succeeded(ResultCode);
  ResultMessage := '';
  if Failed(ResultCode) then
  begin
    SetLength(RError, 128);
    Size := AmGetErrorText(ResultCode, @RError[1], 128);
    if Size <> 0 then
    begin
      SetLength(RError, Size);
      ResultMessage := RError;
    end;
    // Next need DLL which is not always present ....
//    RString      := DxGetErrorString9(Code);
//    RDescription := DxGetErrorDescription9(Code);
//    ShowMessage('DirectShow error:'#13#13 +
//                 RString + #13 +
//                 RDescription);
  end;
end;

{------------------------------------------------------------------------------
  Params  : <UnknownGraph>  Graph to add to ROT
  Returns : <Result>        Result (S_OK if success)
            <RotEntry>      Assigned number in ROT

  Descript: Add graph to Running Object Table so we can connect to it when
            it is running.
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.AddToRot(UnknownGraph: IUnknown; var RotEntry: LongInt): HRESULT;
var
  Moniker: IMoniker;
  Rot    : IRunningObjectTable;
  Wsz    : WideString;
  Wsz2   : WideString;
  Hr     : HRESULT;
begin
  try
    Hr := GetRunningObjectTable(0, ROT);
    if Failed(Hr) then
    begin
      Result := E_FAIL;
      Exit;
    end;
    Wsz  := format('FilterGraph %p pid %8.8x', [Pointer(UnknownGraph), GetCurrentProcessId]);
    Wsz2 := '!';
    Hr := CreateItemMoniker(@Wsz2[1], @Wsz[1], Moniker);
    if Succeeded(Hr) then
    begin
      Hr := ROT.Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, UnknownGraph, Moniker, RotEntry);
    end;
    Result := Hr;
  except
    Result := E_FAIL;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <RotEntry>      Assigned number in ROT
  Returns : -

  Descript: Remove graph from Running Object Table
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.RemoveFromRot(RotEntry: LongInt);
var
  Rot: IRunningObjectTable;
  Hr : HRESULT;
begin
  try
    Hr := GetRunningObjectTable(0, Rot);
    if Succeeded(Hr) then
      Rot.Revoke(RotEntry);
  except
  end;
end;

{------------------------------------------------------------------------------
  Params  : <GraphFile>    Name of graph file
  Returns : <Result>       True if success
            <FilterGraph>  Filter graph to load into

  Descript: Load a DirectShow graph (.GRF) file
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.LoadGraphFile(var FilterGraph: IFilterGraph2; GraphFile: AnsiString): Boolean;
var
  Storage : IStorage;
  AStream : IStream;
  Hr      : HRESULT;
  Name    : WideString;
begin
  Result := False;
  try
    if GraphFile <> '' then
    begin
      // Create document file that will hold the GRF file
      Name := GraphFile;
      Hr := StgOpenStorage(PWideChar(Name), nil, STGM_TRANSACTED or STGM_READ or STGM_SHARE_DENY_WRITE, nil, 0, Storage);
      if Succeeded(Hr) then
      try
        // Create a stream to store
        Hr := Storage.OpenStream('ActiveMovieGraph', nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, AStream);
        if Succeeded(Hr) then
        try
          // Using the IPersisStream.Load method which converts a peristent object into a stream
          Hr := (FilterGraph as IPersistStream).Load(AStream);
        finally
          AStream := nil;
        end;
      finally
        Storage := nil;
      end;
      Result := Succeeded(Hr);
    end;
  except
  end;
end;

{------------------------------------------------------------------------------
  Params  : <FilterGraph>  Graph to save as file
            <GraphFile>    Name of graph file
  Returns : <Result>       True if success

  Descript: Save to a DirectShow graph (.GRF) file
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.SaveGraphFile(FilterGraph: IFilterGraph2; GraphFile: AnsiString): Boolean;
var
  Storage: IStorage;
  AStream: IStream;
  Hr     : HRESULT;
  Name   : WideString;
begin
  Result := False;
  try
    if (GraphFile <> '') then
    begin
      // Create document file that will hold the GRF file
      Name := GraphFile;
      Hr := StgCreateDocFile(PWideChar(Name), STGM_CREATE or STGM_TRANSACTED or STGM_READWRITE or STGM_SHARE_EXCLUSIVE, 0, Storage);
      if Succeeded(Hr) then
      try
        // Create a stream to store
        Hr := Storage.CreateStream('ActiveMovieGraph', STGM_WRITE or STGM_CREATE or STGM_SHARE_EXCLUSIVE, 0, 0, AStream);
        if Succeeded(Hr) then
        try
          // Using the IPersisStream.Save method which converts a stream into a persistent object
          Hr := (FilterGraph as IPersistStream).Save(AStream, True);
        finally
          AStream := nil;
        end;
        if Succeeded(Hr) then
          Hr := Storage.Commit(STGC_DEFAULT);
      finally
        Storage := nil;
      end;
      Result := Succeeded(Hr);
    end;
  except
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Owner>           Handle to window for video
            <GraphicsFile>    .GRF file to use
                              If empty the graph is manually created
            <Options>         Options to use (see constants)
            <Devices>         Number of devices ('inputs') e.g. for PIP
  Returns : <Result>          TRUE if success, FALSE if failure
            <ErrorMessage>    Error message (if any)

  Descript: Setup direct show graph
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.DirectShowStart(Owner: HWND; GraphicsFile: string; Options: Dword; Devices: Integer; var ErrorMessage: string): Boolean;
var
  Hr             : HRESULT;
  Pins           : IPin;
//  Pins2          : IPin;
  PinsIn         : IPin;
  PinsOut        : IPin;
  PinsEnum       : IEnumPins;
//  PinsEnum2      : IEnumPins;
  PinsInfo       : TPinInfo;
  MediaType      : AM_MEDIA_TYPE;
  EnumFilters    : IEnumFilters;
  BaseFilter     : IBaseFilter;
  AllPins        : array[0..99] of IPin;
  AllPinsCount   : Integer;
  Index1         : Integer;
  EnumMediaTypes : IEnumMediaTypes;
  PMediaType     : PAMMediaType;
  APin           : IPin;
  DestinationRect: TRect;
  FlipRect7      : TNormalizedRect;
  FlipRect9      : TVMR9NormalizedRect;
  FilterState    : TFilterState;
  ResultMessage  : string;
//  VideoAnalysis  : IBaseFilter;
  Success        : Boolean;
  ManualBuild    : Boolean;
  {------------------------------------------------------------------------------
    Params  : <FilterGraph>  Filter graph to use
              <Name>         Name to find
    Returns : <Result>
              <Filter>       Filter found

    Descript: Find filter by name
    Notes   :
   ------------------------------------------------------------------------------}
  function FindFilterByName(FilterGraph: IFilterGraph2; Name: WideString; var Filter: IBaseFilter): HRESULT;
  begin
    Result := FilterGraph.FindFilterByName(PWideChar(Name), Filter);
  end;

  {------------------------------------------------------------------------------
    Params  : <FilterGraph>     Filter graph to use
              <FilterName>      Filter which has the pin
              <PinName>         Name of the pin to render
    Returns : <Result>          TRUE if success, FALSE if failure
              <ResultMessage>   Error message

    Descript: Render a pin
    Notes   :
   ------------------------------------------------------------------------------}
  function RenderPin(FilterGraph: IFilterGraph2; FilterName: WideString; PinName: WideString; var ResultMessage: string): Boolean;
  var
    Hr      : HRESULT;
    Filter  : IBaseFilter;
    EnumPins: IEnumPins;
    Pin     : IPin;
    pInfo   : TPinInfo;
  begin
    Result := False;
    try
      ResultMessage := '';
      Hr := FindFilterByName(FilterGraph, FilterName, Filter);
      if Succeeded(Hr) then
      begin
        Filter.EnumPins(EnumPins);
        // Go through all pins until we find it
        EnumPins.Reset;
        while Succeeded(EnumPins.Next(1, Pin, nil)) do
        begin
          Pin.QueryPinInfo(pInfo);
          pInfo.pFilter := nil;
          if (pInfo.achName = PinName) then
          begin
            // Only render if not connected
            if (FOptions and CDirectShowMethodRenderEx) <> 0 then
            begin
              Hr := FilterGraph.RenderEx(Pin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, nil);
              if Failed(Hr) then
                Hr := FilterGraph.Render(Pin);
            end
            else
              Hr := FilterGraph.Render(Pin);
            // Failing of rendering can be normal since it might be already connected ...
            // Reconnecting it should succeed though
            if Failed(Hr) then
              Hr := FilterGraph.ReConnectEx(Pin, nil);
            if not DirectShowCheck(Hr, ResultMessage) then
              Result := False
            else
              Result := True;
            Pin := nil;
            Exit;
          end;
        end;
      end
      else
        DirectShowCheck(Hr, ResultMessage);
    except
    end;
  end;

  {------------------------------------------------------------------------------
    Params  : <FilterGraph>     Filter graph to use
              <Pin>             Pin interface to render
    Returns : <Result>          TRUE if success, FALSE if failure
              <ResultMessage>   Error message

    Descript: Render a pin
    Notes   :
   ------------------------------------------------------------------------------}
  function RenderPin2(FilterGraph: IFilterGraph2; Pin: IPin; var ResultMessage: string): Boolean;
  var
    Hr      : HRESULT;
    pInfo   : TPinInfo;
  begin
    Result := False;
    if not Assigned(Pin) then
      Exit;
    try
      ResultMessage := '';
      Pin.QueryPinInfo(pInfo);
      // Only render if not connected
      if (FOptions and CDirectShowMethodRenderEx) <> 0 then
      begin
        Hr := FilterGraph.RenderEx(Pin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, nil);
        if Failed(Hr) then
          Hr := FilterGraph.Render(Pin);
      end
      else
        Hr := FilterGraph.Render(Pin);
      // Failing of rendering can be normal since it might be already connected ...
      // Reconnecting it should succeed though
      if Failed(Hr) then
        Hr := FilterGraph.ReConnectEx(Pin, nil);
      if not DirectShowCheck(Hr, ResultMessage) then
        Result := False
      else
        Result := True;
    except
    end;
  end;

  {------------------------------------------------------------------------------
    Params  : <FilterGraph>    Filter graph to use
              <Filter>         Base filter
              <Name>           Name of filter to add
    Returns : <Result>

    Descript: Add filter by name
    Notes   :
   ------------------------------------------------------------------------------}
  function AddFilter(FilterGraph: IFilterGraph2; Filter: IBaseFilter; Name: WideString): HRESULT;
  begin
    Result := FilterGraph.AddFilter(Filter, PWideChar(Name));
  end;

  {------------------------------------------------------------------------------
    Params  : <FilterGraph>    Filter graph to use
              <PinOut>         Output pin
              <PinIn>          Input pin
    Returns : <Result>

    Descript: Connect pins
    Notes   :
   ------------------------------------------------------------------------------}
  function Connect(FilterGraph: IFilterGraph2; PinOut: IPin; PinIn: IPin): HRESULT;
  begin
    Result := FilterGraph.Connect(PinOut, PinIn);
  end;

  {------------------------------------------------------------------------------
    Params  : <FilterGraph>    Filter graph to use
    Returns : <Result>

    Descript: Set default clock source
    Notes   :
   ------------------------------------------------------------------------------}
  function SetDefaultSyncSource(FilterGraph: IFilterGraph2): HRESULT;
  begin
    Result := FilterGraph.SetDefaultSyncSource;
  end;

  {------------------------------------------------------------------------------
    Params  : <FilterGraph>    Filter graph to use
              <Index>          Index number (for name 'calling')
    Returns : <Result>

    Descript: Add a Major Push Source and MPEG2 demultiplexer filter to the graph
    Notes   :
   ------------------------------------------------------------------------------}
  function AddSourceDemultiplexerFilters(FilterGraph: IFilterGraph2; Index: Integer): Boolean;
  begin
    Result := False;
    if (Index < Low(FSourceDemuxFilters)) or (Index > High(FSourceDemuxFilters)) then
      Exit;
    // The push source
    Hr := CoCreateInstance(CLSID_MajorPushSource, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, FSourceDemuxFilters[Index].FFilterPushSource);
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      // Try register it (it might not have registered)
      ShellExecute(Application.Handle, nil, 'regsvr32', '/s MajorPushSource.ax', nil, SW_SHOW);
      // Make sure register is done
      Sleep(100);
      // Retry
      Hr := CoCreateInstance(CLSID_MajorPushSource, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, FSourceDemuxFilters[Index].FFilterPushSource);
      if not DirectShowCheck(Hr, ResultMessage) then
      begin
        ErrorMessage := 'Could not use the push source filter.';
        ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
        Exit;
      end;
    end;
    Hr := AddFilter(FilterGraph, FSourceDemuxFilters[Index].FFilterPushSource, format('Major Push Source Device #%d', [Index]));
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := format('Adding push source filter error $%8.8x.', [Hr]);
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    // Get the interface (which we will be using when pushing the data)
    Hr := FSourceDemuxFilters[Index].FFilterPushSource.QueryInterface(T_IID_MajorPushSource, FSourceDemuxFilters[Index].FFilterPushSourceI);
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := format('Getting interface to push source filter error $%8.8x.', [Hr]);
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    // The MPEG-2 demultiplexer
    Hr := CoCreateInstance(CLSID_MPEG2Demultiplexer, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, FSourceDemuxFilters[Index].FFilterMPEG2Demultiplexer);
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := 'Could not use the MPEG-2 Demultiplexer filter.';
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    Hr := AddFilter(FilterGraph, FSourceDemuxFilters[Index].FFilterMPEG2Demultiplexer, format('MPEG-2 Demultiplexer Device #%d', [Index]));
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := format('Adding MPEG-2 Demultiplexer filter error $%8.8x.', [Hr]);
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    // Now we must create the output pins on the MPEG-2 demultiplexer
    // The video pin
    MediaType.MajorType  := MEDIATYPE_Video;
    MediaType.SubType    := MEDIASUBTYPE_MPEG2_VIDEO;
    if (FOptions and CDirectShowMethodSetFormat) = 0 then
      MediaType.FormatType := GUID_NULL
    else
      MediaType.FormatType := FORMAT_MPEG2_VIDEO;
    Hr := (FSourceDemuxFilters[Index].FFilterMPEG2Demultiplexer as IMPEG2Demultiplexer).CreateOutputPin(MediaType, 'Video', FSourceDemuxFilters[Index].FVideoPin);
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := 'Could not create the video output pin for the MPEG-2 Demultiplexer.';
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    // The audio pin
    MediaType.MajorType  := MEDIATYPE_Audio;
    MediaType.SubType    := MEDIASUBTYPE_MPEG2_AUDIO;
    if (FOptions and CDirectShowMethodSetFormat) = 0 then
      MediaType.FormatType := GUID_NULL
    else
      MediaType.FormatType := FORMAT_MPEG2Audio;
    Hr := (FSourceDemuxFilters[Index].FFilterMPEG2Demultiplexer as IMPEG2Demultiplexer).CreateOutputPin(MediaType, 'Audio', FSourceDemuxFilters[Index].FAudioPin);
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := 'Could not create the audio output pin for the MPEG-2 Demultiplexer.';
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    // The audio pin (AC3)
    if (FOptions and CDirectShowMethodNoAC3Pin) = 0 then
    begin
      MediaType.MajorType  := MEDIATYPE_Audio;
      MediaType.SubType    := MEDIASUBTYPE_DOLBY_AC3;
      if (FOptions and CDirectShowMethodSetFormat) = 0 then
        MediaType.FormatType := GUID_NULL
      else
        MediaType.FormatType := FORMAT_DolbyAC3;
      Hr := (FSourceDemuxFilters[Index].FFilterMPEG2Demultiplexer as IMPEG2Demultiplexer).CreateOutputPin(MediaType, 'AudioAC3', FSourceDemuxFilters[Index].FAudioAc3Pin);
      if not DirectShowCheck(Hr, ResultMessage) then
      begin
        ErrorMessage := 'Could not create the AC3 audio output pin for the MPEG-2 Demultiplexer.';
        ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
        Exit;
      end;
    end;

    // At this point there are the following filters in the graph with the
    // required pins:
    // . Push source
    // . MPEG2 demultiplexer
    // The push source must now be connected to the MPEG2 demultiplexer

    // Enumerate the push source pins
    PinsIn   := nil;
    PinsOut  := nil;
    PinsEnum := nil;
    Hr := FSourceDemuxFilters[Index].FFilterPushSource.EnumPins(PinsEnum);
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := 'Could not enumerate the push source pins.';
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    // Go through the pins and record the ones we are interested in
    PinsEnum.Reset;
    while PinsEnum.Next(1, Pins, nil) = S_OK do
    begin
      Pins.QueryPinInfo(PinsInfo);
      PinsInfo.pFilter := nil;
      if UpperCase(PinsInfo.achName) = 'OUTPUT' then
        PinsOut := Pins;
    end;
    Pins     := nil;
    PinsEnum := nil;

    // Enumerate the MPEG2 demultiplexer pins
    Hr := FSourceDemuxFilters[Index].FFilterMPEG2Demultiplexer.EnumPins(PinsEnum);
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := 'Could not enumerate the MPEG-2 Demultiplexer pins.';
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    // Go through the pins and record the ones we are interested in
    PinsEnum.Reset;
    while PinsEnum.Next(1, Pins, nil) = S_OK do
    begin
      Pins.QueryPinInfo(PinsInfo);
      PinsInfo.pFilter := nil;
      if UpperCase(PinsInfo.achName) = 'MPEG-2 STREAM' then
        PinsIn := Pins;
    end;
    Pins     := nil;
    PinsEnum := nil;

    // Connect the pins between push source and MPEG2 demultiplexer
    if not Assigned(PinsIn) or not Assigned(PinsOut) then
    begin
      PinsIn  := nil;
      PinsOut := nil;
      ErrorMessage := 'Could not find correct pins push source/MPEG-2 Demultiplexer.';
      Exit;
    end;
    Hr := Connect(FilterGraph, PinsOut, PinsIn);
    PinsIn  := nil;
    PinsOut := nil;
    if not DirectShowCheck(Hr, ResultMessage) then
    begin
      ErrorMessage := 'Could not connect pins push source/MPEG-2 Demultiplexer.';
      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
      Exit;
    end;
    Result := True;
  end;

  {------------------------------------------------------------------------------
    Params  : <FilterGraph>    Filter graph to use
              <Index>          Index number (for name 'calling')
    Returns : <Result>         True if filters and video/audio pin found

    Descript: Scan the graph for push source and MPEG2 demultiplexer filters
              and update FSourceDemuxFilters accordingly

    Notes   : Used for scanning a loaded graph
   ------------------------------------------------------------------------------}
  function FindSourceDemultiplexerFilters(FilterGraph: IFilterGraph2; Index: Integer): Boolean;
  begin
    Result := False;
    if (Index < Low(FSourceDemuxFilters)) or (Index > High(FSourceDemuxFilters)) then
      Exit;
    Hr := FindFilterByName(FFilterGraphStreamIn, format('Major Push Source Device #%d', [Index]), FSourceDemuxFilters[Index1].FFilterPushSource);
    if (Index1 = 0) and Failed(Hr) then
      Hr := FindFilterByName(FFilterGraphStreamIn, 'Major Push Source', FSourceDemuxFilters[Index].FFilterPushSource);
    if Succeeded(Hr) then
    begin
      // Get the interface (which we will be using when pushing the data)
      Hr := FSourceDemuxFilters[Index].FFilterPushSource.QueryInterface(T_IID_MajorPushSource, FSourceDemuxFilters[Index].FFilterPushSourceI);
      if not DirectShowCheck(Hr, ResultMessage) then
      begin
        ErrorMessage := format('Getting interface to push source filter error $%8.8x.', [Hr]);
        ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
        Exit;
      end;
      // Find other filters
      Hr := FindFilterByName(FFilterGraphStreamIn, format('MPEG-2 Demultiplexer Device #%d', [Index]), FSourceDemuxFilters[Index1].FFilterMPEG2Demultiplexer);
      if (Index = 0) and Failed(Hr) then
        Hr := FindFilterByName(FFilterGraphStreamIn, 'MPEG-2 Demultiplexer', FSourceDemuxFilters[0].FFilterMPEG2Demultiplexer);
      if Succeeded(Hr) then
      begin
        if Failed(FSourceDemuxFilters[Index1].FFilterMPEG2Demultiplexer.FindPin('Video', FSourceDemuxFilters[Index].FVideoPin)) then
          FSourceDemuxFilters[Index].FVideoPin := nil;
        if Failed(FSourceDemuxFilters[Index1].FFilterMPEG2Demultiplexer.FindPin('Audio', FSourceDemuxFilters[Index].FAudioPin)) then
          FSourceDemuxFilters[Index].FAudioPin := nil;;
        if Failed(FSourceDemuxFilters[Index1].FFilterMPEG2Demultiplexer.FindPin('AudioAC3', FSourceDemuxFilters[Index].FAudioAc3Pin)) then
          FSourceDemuxFilters[Index].FAudioAc3Pin := nil;;
        // Do not take AC3 into consideration
        Result := (Assigned(FSourceDemuxFilters[Index].FVideoPin) and Assigned(FSourceDemuxFilters[Index].FAudioPin));
      end;
    end;
  end;


begin
  try
    Result := False;
    // Note: 'Disable' multiple support
    Devices := 1;
    if (Devices < (Low(FSourceDemuxFilters)+1)) or (Devices > (High(FSourceDemuxFilters)+1)) then
      Exit;
    // Make sure a previously started DirectShow interface is stopped and released
    DirectShowStop;
    ErrorMessage := '';
    DirectShowAssign(Owner);
    try
      FOptions := Options;

      {------------------------------------------------------------------------------
        Descript: Create the filter managers
       ------------------------------------------------------------------------------}
      Hr := CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER, IID_IFilterGraph2, FFilterGraphStreamIn);
      if not DirectShowCheck(Hr, ResultMessage) then
      begin
        ErrorMessage := format('CoCreateInstance filter graph manager 1 error $%8.8x.', [Hr]);
        ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
        Exit;
      end;
(*      if (FOptions and CDirectShowMethodNoStreamBuffer) = 0 then
      begin
        Hr := CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER, IID_IFilterGraph2, FFilterGraphStreamOut);
        if not DirectShowCheck(Hr, ResultMessage) then
        begin
          ErrorMessage := format('CoCreateInstance filter graph manager 2 error $%8.8x.', [Hr]);
          ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
          Exit;
        end;
      end
      else
        FFilterGraphStreamOut := FFilterGraphStreamIn;
*)      

      {------------------------------------------------------------------------------
        Descript: ROT
       ------------------------------------------------------------------------------}
      if (FOptions and CDirectShowMethodDoNotAddToRot) = 0 then
      begin
        Hr := AddToRot(FFilterGraphStreamIn, FRotEntry[0]);
        if Failed(Hr) then
          FRotEntry[0] := -1;
(*        if (FOptions and CDirectShowMethodNoStreamBuffer) = 0 then
        begin
          Hr := AddToRot(FFilterGraphStreamOut, FRotEntry[1]);
          if Failed(Hr) then
            FRotEntry[1] := -1;
        end;
*)        
      end;

      {------------------------------------------------------------------------------
        Descript: Load filter graph
       ------------------------------------------------------------------------------}
      ManualBuild := not FileExists(GraphicsFile);
      if not ManualBuild then
      begin
        // When a StreammBufferSink/Source is used, load the graph
        // in the 'input' filter graph
//        if (FOptions and CDirectShowMethodNoStreamBuffer) <> 0 then
//          Success := LoadGraphFile(FFilterGraphStreamOut, GraphicsFile)
//        else
          Success := LoadGraphFile(FFilterGraphStreamIn, GraphicsFile);
        if not Success then
        begin
          ErrorMessage := format('Loading graph file "%s" error.', [GraphicsFile]);
          ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
          Exit;
        end;
      end;

      {------------------------------------------------------------------------------
        Descript: VMR
       ------------------------------------------------------------------------------}
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer: Hr := NOERROR;
        CDirectShowMethodVideoRenderer7:
          begin
            // Try locating if VMR already present
            Hr := FindFilterByName(FFilterGraphStreamIn, 'Video Mixing Renderer 7', FFilterVmr);
            if Failed(Hr) then
            begin
              Hr := FindFilterByName(FFilterGraphStreamIn, 'VMR7', FFilterVmr);
              if Failed(Hr) then
              begin
                // Not present, create it
                Hr := CoCreateInstance(CLSID_VideoMixingRenderer, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, FFilterVmr);
                if Succeeded(Hr) then
                begin
                  Hr := AddFilter(FFilterGraphStreamIn, FFilterVmr, 'Video Mixing Renderer 7');
                  if Failed(Hr) then
                    Hr := AddFilter(FFilterGraphStreamIn, FFilterVmr, 'VMR7');
                end;
              end;
            end;
          end;
        CDirectShowMethodVideoRenderer9:
          begin
            // Try locating if VMR already present (Note: will ALWAYS fail if nothing loaded)
            Hr := FindFilterByName(FFilterGraphStreamIn, 'Video Mixing Renderer 9', FFilterVmr);
            if Failed(Hr) then
            begin
              Hr := FindFilterByName(FFilterGraphStreamIn, 'VMR9', FFilterVmr);
              if Failed(Hr) then
              begin
                // Not present, create it
                Hr := CoCreateInstance(CLSID_VideoMixingRenderer9, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, FFilterVmr);
                if Succeeded(Hr) then
                begin
                  Hr := AddFilter(FFilterGraphStreamIn, FFilterVmr, 'Video Mixing Renderer 9');
                  if Failed(Hr) then
                    Hr := AddFilter(FFilterGraphStreamIn, FFilterVmr, 'VMR9');
                end;
              end;
            end;
          end;
      else
        Hr := E_NOTIMPL;
      end;
      if not DirectShowCheck(Hr, ResultMessage) then
      begin
        ErrorMessage := format('CoCreateInstance video mixing renderer error $%8.8x.', [Hr]);
        ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
        Exit;
      end;

      {------------------------------------------------------------------------------
        Descript: Set VMR mode
       ------------------------------------------------------------------------------}
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer: ;
        CDirectShowMethodVideoRenderer7:
          begin
            // We need to set the number of streams to be able to use the mixer and this must before setting window mode
            (FFilterVmr as IVMRFilterConfig).SetNumberOfStreams(Devices);
            // Flip
            FlipRect7.Left   := 0;
            FlipRect7.Top    := 0;
            FlipRect7.Right  := 1;
            FlipRect7.Bottom := 1;
            // Note: Using -1 instead of swapping does not work correctly
            if (FOptions and CDirectShowMethodVideoRendererFlipX) <> 0 then
            begin
              FlipRect7.Left  := 1;
              FlipRect7.Right := 0;
            end;
            if (FOptions and CDirectShowMethodVideoRendererFlipY) <> 0 then
            begin
              FlipRect7.Top    := 1;
              FlipRect7.Bottom := 0;
            end;
            (FFilterVmr as IVMRMixerControl).SetOutputRect(0, FlipRect7);
            // Window(less) mode
            if (FOptions and CDirectShowMethodVmrSpecials) = CDirectShowMethodVideoRendererWindowless then
            begin
              (FFilterVmr as IVMRFilterConfig).SetRenderingMode(VMRMode_Windowless);
              Hr := FFilterVmr.QueryInterface(IID_IVMRWindowlessControl, FWindowlessControl);
              if Succeeded(Hr) then
                IVMRWindowlessControl(FWindowlessControl).SetVideoClippingWindow(FOwner)
              else
                (FFilterVmr as IVMRFilterConfig).SetRenderingMode(VMRMode_Windowed);
            end
            else
              (FFilterVmr as IVMRFilterConfig).SetRenderingMode(VMRMode_Windowed);
          end;
        CDirectShowMethodVideoRenderer9:
          begin
            // We need to set the number of streams to be able to use the mixer and this must before setting window mode
            (FFilterVmr as IVMRFilterConfig9).SetNumberOfStreams(Devices);
            for Index1 := 0 to Devices-1 do
            begin
              // Flip
              FlipRect9.Left   := 0;
              FlipRect9.Top    := 0;
              FlipRect9.Right  := 1;
              FlipRect9.Bottom := 1;
              // Note: Using -1 instead of swapping does not work correctly
              if (FOptions and CDirectShowMethodVideoRendererFlipX) <> 0 then
              begin
                FlipRect9.Left  := 1;
                FlipRect9.Right := 0;
              end;
              if (FOptions and CDirectShowMethodVideoRendererFlipY) <> 0 then
              begin
                FlipRect9.Top    := 1;
                FlipRect9.Bottom := 0;
              end;

(*              FlipRect9.Left   := Index1 * (1 / Devices);
              FlipRect9.Top    := Index1 * (1 / Devices);
              FlipRect9.Right  := FlipRect9.Left + (1 / Devices);
              FlipRect9.Bottom := FlipRect9.Top + (1 / Devices);
*)
              (FFilterVmr as IVMRMixerControl9).SetOutputRect(Index1, @FlipRect9);

(*
              (FFilterVmr as IVMRMixerControl9).SetAlpha(Index1, 1);
              (FFilterVmr as IVMRMixerControl9).SetZOrder(Index1, Index1);
*)
            end;
            // Window(less) mode
            if (FOptions and CDirectShowMethodVmrSpecials) = CDirectShowMethodVideoRendererWindowless then
            begin
              (FFilterVmr as IVMRFilterConfig9).SetRenderingMode(VMRMode_Windowless);
              Hr := FFilterVmr.QueryInterface(IID_IVMRWindowlessControl9, FWindowlessControl);
              if Succeeded(Hr) then
                IVMRWindowlessControl9(FWindowlessControl).SetVideoClippingWindow(FOwner)
              else
                (FFilterVmr as IVMRFilterConfig9).SetRenderingMode(VMRMode_Windowed);
            end
            else
              (FFilterVmr as IVMRFilterConfig9).SetRenderingMode(VMRMode_Windowed);
          end;
      end;

      {------------------------------------------------------------------------------
        Descript: Building the graph
       ------------------------------------------------------------------------------}
      if ManualBuild then
      begin
        {------------------------------------------------------------------------------
          Descript: Building a graph manually
         ------------------------------------------------------------------------------}
        // No graph filter file: create the graph manually (at least the major parts)
        // Since we create the graph manually, the first we create is the
        // default one
        FSourceDemuxDefault := Low(FSourceDemuxFilters);
        for Index1 := 0 to Devices-1 do
          if not AddSourceDemultiplexerFilters(FFilterGraphStreamIn, Index1) then
            Exit;


(*
        // The push source and MPEG2 demultiplexer are now connected.
        // When a stream buffer sink/source is used, then we must connect the
        // MPEG2 demultiplexer outputs to the StreamBufferSink inputs
        if (FOptions and CDirectShowMethodNoStreamBuffer) = 0 then
        begin
          // Create the Video Analyzer filter (for playback rates faster than 4*
          // and reverse playback)
          Hr := CoCreateInstance(CLSID_Mpeg2VideoStreamAnalyzer, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, VideoAnalysis);
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := format('CoCreateInstance filter video analyzer error $%8.8x.', [Hr]);
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;

          // Create the stream buffer source and sink
          Hr := CoCreateInstance(CLSID_StreamBufferSink, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, FFilterStreamBufferSink);
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := format('CoCreateInstance filter buffer sink error $%8.8x.', [Hr]);
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;

          Hr := CoCreateInstance(CLSID_StreamBufferSource, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, FFilterStreamBufferSource);
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := format('CoCreateInstance filter buffer source error $%8.8x.', [Hr]);
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;

          // Add filters to the graphs
          Hr := AddFilter(FFilterGraphStreamOut, VideoAnalysis, 'Video Analyzer');
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := 'Could not add Video Analyzer filter to filter graph.';
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;
          Hr := AddFilter(FFilterGraphStreamOut, FFilterStreamBufferSink, 'Stream Buffer Sink');
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := 'Could not add StreamBufferSink filter to filter graph.';
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;
          Hr := AddFilter(FFilterGraphStreamIn, FFilterStreamBufferSource, 'Stream Buffer Source');
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := 'Could not add StreamBufferSource filter to filter graph.';
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;

          // Connect the MPEG2 output pins to the stream buffer sink
          // The video pin of the MPEG2 demultiplexer connects first to
          // the video analyzer filter ...
          // Enumerate the MPEG2 demultiplexer pins and find the output pins
          Hr := FFilterMPEG2Demultiplexer.EnumPins(PinsEnum);
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := 'Could not enumerate the MPEG-2 Demultiplexer pins.';
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;
          // Go through the pins and record the ones we are interested (all outputs) in
          AllPinsCount := 0;
          APin := nil;
          PinsEnum.Reset;
          while PinsEnum.Next(1, Pins, nil) = S_OK do
          begin
            Pins.QueryPinInfo(PinsInfo);
            PinsInfo.pFilter := nil;
            if PinsInfo.dir = PINDIR_OUTPUT then           // Must be output
            begin
              if UpperCase(PinsInfo.achName) = 'VIDEO' then
              begin
                APin := Pins;                              // Video pin is special
              end
              else
              begin
                AllPins[AllPinsCount] := Pins;
                Inc(AllPinsCount);
              end;
            end;
          end;
          Pins     := nil;
          PinsEnum := nil;
          // Check for video pin (there should be one ...
          if Assigned(APin) then
          begin
            // Connect video pin to video analyzer input
            VideoAnalysis.EnumPins(PinsEnum);
            PinsEnum.Reset;
            while PinsEnum.Next(1, Pins, nil) = S_OK do
            begin
              Pins.QueryPinInfo(PinsInfo);
              if PinsInfo.dir = PINDIR_INPUT then
              begin
                Hr := Connect(FFilterGraphStreamOut, APin, Pins);
                if not DirectShowCheck(Hr, ResultMessage) then
                begin
                  ErrorMessage := 'Could not connect pin to video analyzer.';
                  ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
                  Exit;
                end;
                Break;
              end;
              Pins     := nil;
            end;
            PinsEnum := nil;
            Pins     := nil;
            APin     := nil;
            // Connect the output of the video analyzer to the stream buffer sink
            VideoAnalysis.EnumPins(PinsEnum);
            PinsEnum.Reset;
            while PinsEnum.Next(1, Pins, nil) = S_OK do
            begin
              Pins.QueryPinInfo(PinsInfo);
              if PinsInfo.dir = PINDIR_OUTPUT then
              begin
                // We found the output pin, now find a free pin on the stream
                // buffer sink
                FFilterStreamBufferSink.EnumPins(PinsEnum2);
                PinsEnum2.Reset;
                // Find first free input pin
                while PinsEnum2.Next(1, Pins2, nil) = S_OK do
                begin
                  Pins2.QueryPinInfo(PinsInfo);
                  // Must be input and not connected
                  if (PinsInfo.dir = PINDIR_INPUT) and Failed(Pins2.ConnectedTo(PinsIn)) then
                  begin
                    Hr := Connect(FFilterGraphStreamOut, Pins, Pins2);
                    if not DirectShowCheck(Hr, ResultMessage) then
                    begin
                      ErrorMessage := 'Could not connect pin between video analyzer and stream buffer sink.';
                      ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
                      Exit;
                    end;
                    Break;
                  end;
                  Pins2 := nil;
                end;
                PinsEnum2 := nil;
                Break;
              end;
              Pins := nil;
            end;
            PinsEnum := nil;
          end;
          // Now connect all other pins to the StreamBufferSink. This has to
          // be done one at a time because initially there is only a single pin
          // available.
          for Index1 := 0 to AllPinsCount do
          begin
            FFilterStreamBufferSink.EnumPins(PinsEnum);
            PinsEnum.Reset;
            // Find first free input pin
            while PinsEnum.Next(1, Pins, nil) = S_OK do
            begin
              Pins.QueryPinInfo(PinsInfo);
              // Must be input and not connected
              if (PinsInfo.dir = PINDIR_INPUT) and Failed(Pins.ConnectedTo(PinsIn)) then
              begin
                Connect(FFilterGraphStreamOut, AllPins[Index1], Pins);
                if not DirectShowCheck(Hr, ResultMessage) then
                begin
                  ErrorMessage := format('Failure connecting pin with StreamBufferSink $%8.8x.', [Hr]);
                  ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
                  Exit;
                end;
                Break;
              end;
              PinsIn := nil;
            end;
            Pins            := nil;
            AllPins[Index1] := nil;
            PinsEnum        := nil;
          end;

          // Lock the profile
          // Note: Can not use 'nil'!!! -> will generate exception
          //       Works only with empty string (e.g '') == pointer to empty string
          Hr := (FFilterStreamBufferSink as IStreamBufferSink).LockProfile('');
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := format('Could not lock stream buffer sink, error $%8.8x.', [Hr]);
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;

          // The pins between the push source, MPEG2 demultiplexer and
          // stream buffer sink are now connected
          // We have to let the stream source know that it has to use the
          // stream sink .....
FAILS!!!          Hr := (FFilterStreamBufferSource as IStreamBufferSource).SetStreamSink(FFilterStreamBufferSink as IStreamBufferSink);
          if not DirectShowCheck(Hr, ResultMessage) then
          begin
            ErrorMessage := 'Could not connect stream buffer sink with source.';
            ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
            Exit;
          end;
          // Render all pins of the stream source
          FFilterStreamBufferSource.EnumPins(PinsEnum);
          PinsEnum.Reset;
          // Find first free input pin
          while PinsEnum.Next(1, Pins, nil) = S_OK do
          begin
            if (FOptions and CDirectShowMethodRenderEx) <> 0 then
            begin
              Hr := FFilterGraphStreamIn.RenderEx(Pins, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, nil);
              if Failed(Hr) then
                FFilterGraphStreamIn.Render(Pins);
            end
            else
              FFilterGraphStreamIn.Render(Pins);
            Pins := nil;
          end;
          PinsEnum        := nil;
        end;
*)
        {------------------------------------------------------------------------------
          Descript: Building a graph manually end
         ------------------------------------------------------------------------------}
      end;
      if not ManualBuild then
      begin
        // If the graph was not manually build, then we need to investigate the
        // loaded graph for 'components' of the TSourceDemuxFilter
        //   FFilterMPEG2Demultiplexer
        //   FFilterPushSource
        //   FVideoPin
        //   FAudioPin
        //   FAudioAc3Pin
        // FSourceDemuxDefault is also set to the first valid filters found
        FSourceDemuxDefault := -1;
        for Index1 := 0 to Devices-1 do
          if FindSourceDemultiplexerFilters(FFilterGraphStreamIn, Index1) and (FSourceDemuxDefault = -1) then
            FSourceDemuxDefault := Index1;
        if FSourceDemuxDefault = -1 then
        begin
          ErrorMessage := 'No valid filters/pins found in loaded graph file.';
          ErrorMessage := ErrorMessage + #13#13;
          Exit;
        end;
      end;

      {------------------------------------------------------------------------------
        Descript: Check for required components
       ------------------------------------------------------------------------------}
      if not Assigned(FSourceDemuxFilters[FSourceDemuxDefault].FFilterMPEG2Demultiplexer) then
      begin
        ErrorMessage := 'MPEG-2 Demultiplexer filter not found.';
        ErrorMessage := ErrorMessage + #13#13;
        Exit;
      end;
      if not Assigned(FSourceDemuxFilters[FSourceDemuxDefault].FVideoPin) then
      begin
        ErrorMessage := 'MPEG-2 Demultiplexer video pin not found.';
        ErrorMessage := ErrorMessage + #13#13;
        Exit;
      end;
      if not Assigned(FSourceDemuxFilters[FSourceDemuxDefault].FAudioPin) then
      begin
        ErrorMessage := 'MPEG-2 Demultiplexer audio pin not found.';
        ErrorMessage := ErrorMessage + #13#13;
        Exit;
      end;
      if not Assigned(FSourceDemuxFilters[FSourceDemuxDefault].FFilterPushSource) then
      begin
        ErrorMessage := 'Push source filter not found.';
        ErrorMessage := ErrorMessage + #13#13;
        Exit;
      end;

      {------------------------------------------------------------------------------
        Descript: Render
       ------------------------------------------------------------------------------}
      if (FOptions and CDirectShowMethodDoNotRender) = 0 then
      begin
        // Go through all devices
        for Index1 := 0 to Devices-1 do
        begin
          if Assigned(FSourceDemuxFilters[Index1].FFilterMPEG2Demultiplexer) then
          begin
            if not RenderPin2(FFilterGraphStreamIn, FSourceDemuxFilters[Index1].FAudioPin, ResultMessage) then
            begin
              ErrorMessage := 'MPEG-2 Demultiplexer audio pin rendering error.';
              ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
              Exit;
            end;
            // AC3 might not exist, so skip any errors
            RenderPin2(FFilterGraphStreamIn, FSourceDemuxFilters[Index1].FAudioAC3Pin, ResultMessage);
            if not RenderPin2(FFilterGraphStreamIn, FSourceDemuxFilters[Index1].FVideoPin, ResultMessage) then
            begin
              ErrorMessage := 'MPEG-2 Demultiplexer video pin rendering error.';
              ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
              Exit;
            end;
          end;
        end;
        {------------------------------------------------------------------------------
          Descript: Render all pins of all filters
         ------------------------------------------------------------------------------}
        if (FOptions and CDirectShowMethodRenderAllPins) <> 0 then
        begin
          Hr := FFilterGraphStreamIn.EnumFilters(EnumFilters);
          AllPinsCount := 0;
          // If we don't retrieve all filters/pins before rendering them, then we will not get all filters for some reason
          if Succeeded(Hr) then
          begin
            EnumFilters.Reset;
            while EnumFilters.Next(1, BaseFilter, nil) = S_OK do
            begin
              Hr := BaseFilter.EnumPins(PinsEnum);
              if Succeeded(Hr) then
              begin
                PinsEnum.Reset;
                while PinsEnum.Next(1, Pins, nil) = S_OK do
                begin
                  AllPins[AllPinsCount] := Pins;
                  Inc(AllPinsCount);
                end;
                Pins := nil;
              end;
            end;
            EnumFilters := nil;
          end;
          // We now have all the filters with their pins, so render then
          if AllPinsCount > 0 then
            for Index1 := 0 to AllPinsCount - 1 do
            begin
              if (FOptions and CDirectShowMethodRenderEx) <> 0 then
              begin
                Hr := FFilterGraphStreamIn.RenderEx(AllPins[Index1], AM_RENDEREX_RENDERTOEXISTINGRENDERERS, nil);
                if Failed(Hr) then
                  FFilterGraphStreamIn.Render(AllPins[Index1]);
              end
              else
                FFilterGraphStreamIn.Render(AllPins[Index1]);
              AllPins[Index1] := nil;
            end;
        end;
        {------------------------------------------------------------------------------
          Descript: Render all audio/video pins of all filters
         ------------------------------------------------------------------------------}
        if (FOptions and CDirectShowMethodRenderAllAudioVideo) <> 0 then
        begin
          Hr := FFilterGraphStreamIn.EnumFilters(EnumFilters);
          AllPinsCount := 0;
          // If we don't retrieve all filters/pins before rendering them, then we will not get all filters for some reason
          if Succeeded(Hr) then
          begin
            EnumFilters.Reset;
            while EnumFilters.Next(1, BaseFilter, nil) = S_OK do
            begin
              Hr := BaseFilter.EnumPins(PinsEnum);
              if Succeeded(Hr) then
              begin
                PinsEnum.Reset;
                while (PinsEnum.Next(1, Pins, nil) = S_OK) do
                begin
                  // Pins.QueryDirection(PinDirection);
                  //  if PinDirection = PINDIR_OUTPUT then
                  Pins.QueryPinInfo(PinsInfo);
                  PinsInfo.pFilter := nil;
                  // If not connected
                  if PinsInfo.dir = PINDIR_OUTPUT then     // Must be output
                  begin
                    Hr := Pins.EnumMediaTypes(EnumMediaTypes);
                      // Check media types
                    if Succeeded(Hr) then
                    begin
                      EnumMediaTypes.Reset;
                      while EnumMediaTypes.Next(1, PMediaType, nil) = S_OK do
                      begin
                        if IsEqualGUID(PMediaType.majortype, MEDIATYPE_Video) or
                          IsEqualGUID(PMediaType.majortype, MEDIATYPE_Audio)
                            then
                        begin
                          AllPins[AllPinsCount] := Pins;
                          Inc(AllPinsCount);
                        end;
                        PMediaType := nil;
                      end;
                    end;
                  end;
                end;
                APin := nil;
                Pins := nil;
              end;
            end;
            EnumFilters := nil;
          end;
          // We now have all the filters with their pins, so render then
          if AllPinsCount > 0 then
            for Index1 := 0 to AllPinsCount - 1 do
            begin
              if (FOptions and CDirectShowMethodRenderEx) <> 0 then
              begin
                Hr := FFilterGraphStreamIn.RenderEx(AllPins[Index1], AM_RENDEREX_RENDERTOEXISTINGRENDERERS, nil);
                if Failed(Hr) then
                  FFilterGraphStreamIn.Render(AllPins[Index1]);
              end
              else
                FFilterGraphStreamIn.Render(AllPins[Index1]);
              AllPins[Index1] := nil;
            end;
        end;
      end;

      for Index1 := 0 to Devices-1 do
        if Assigned(FSourceDemuxFilters[Index1].FFilterMPEG2Demultiplexer) then
          FSourceDemuxFilters[Index1].FFilterMPEG2Demultiplexer.SetSyncSource(nil);
      SetDefaultSyncSource(FFilterGraphStreamIn);

      // Get IBasicVideo interface
      FFilterGraphStreamIn.QueryInterface(IID_IBasicVideo, FBasicVideo);

      {------------------------------------------------------------------------------
        Descript: No VMR (default Video Renderer)
       ------------------------------------------------------------------------------}
      if ((FOptions and CDirectShowMethodVmr) = CDirectShowMethodVideoRenderer) or (not Assigned(FWindowlessControl)) then
      begin
        Hr := FFilterGraphStreamIn.QueryInterface(IID_IVideoWindow, FVideoWindow);
        if not DirectShowCheck(Hr, ResultMessage) then
        begin
          ErrorMessage := 'Could not retrieve video window interface.';
          ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
          Exit;
        end;
        Hr := FVideoWindow.put_Owner(FOwner);
        if not DirectShowCheck(Hr, ResultMessage) then
        begin
          ErrorMessage := format('Filter could not set video handle, error $%8.8x.', [Hr]);
          ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
          Exit;
        end;
        Hr := FVideoWindow.put_MessageDrain(FOwner);
        if not DirectShowCheck(Hr, ResultMessage) then
        begin
          ErrorMessage := 'Filter could not set messages handle.';
          ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
          Exit;
        end;
        Hr := FVideoWindow.put_WindowStyle(WS_CHILD or WS_CLIPSIBLINGS or
          WS_CLIPCHILDREN);
        if not DirectShowCheck(Hr, ResultMessage) then
        begin
          ErrorMessage := 'Filter could not set window style.';
          ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
          Exit;
        end;

        GetClientRect(FOwner, DestinationRect);
        Hr := FVideoWindow.SetWindowPosition(0, 0, DestinationRect.Right - DestinationRect.Left, DestinationRect.Bottom - DestinationRect.Top);
        if not DirectShowCheck(Hr, ResultMessage) then
        begin
          ErrorMessage := 'Filter could not set window size.';
          ErrorMessage := ErrorMessage + #13#13 + ResultMessage;
          Exit;
        end;
      end;
      // Media controls
      (FFilterGraphStreamIn as IMediaControl).Stop;
      (FFilterGraphStreamIn as IMediaControl).Run;
      (FFilterGraphStreamIn as IMediaControl).GetState(1000, FilterState);
      // Indicate success
      Result := True;
    finally
      // Make sure that at a failure all resources are freed
      if not Result then
        DirectShowStop;
    end;
  except
    Result := False;
    DirectShowStop;
    ShowMessage('DirectShow Start error');
  end;
end;

function TDirectShowGraph.DirectShowBlendImage(Bitmap: HDC; Src: TRect; Blend: Single; ColorKey: COLORREF): Boolean;
begin
  Result := False;
  if (FOptions and CDirectShowMethodVmr) = CDirectShowMethodVideoRenderer then
    Exit;
  Result := BlendApplicationImage(Bitmap, Src, Blend, ColorKey);
end;

{------------------------------------------------------------------------------
  Params  : <Owner>  Handle to window for video
  Returns : -

  Descript: Constructor of object.
  Notes   :
 ------------------------------------------------------------------------------}

constructor TDirectShowGraph.Create(Owner: HWND);
begin
  inherited Create;
  DirectShowAssign(Owner);
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Destructor of object.
  Notes   :
 ------------------------------------------------------------------------------}

destructor TDirectShowGraph.Destroy;
begin
  DirectShowStop;
  inherited Destroy;
end;

{------------------------------------------------------------------------------
  Params  : <Owner>  Handle to window for video
  Returns : -

  Descript: Initialize of object.
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowAssign(Owner: HWND);
var
  Loop: Integer;
begin
  FOwner    := Owner;
  FLogFile  := INVALID_HANDLE_VALUE;
  FRotEntry[0] := -1;
  FRotEntry[1] := -1;
  FFilterGraphStreamIn  := nil;
//  FFilterStreamBufferSource := nil;
//  FFilterStreamBufferSink   := nil;
  FBasicVideo           := nil;
  FFilterVmr            := nil;
  for Loop := Low(FSourceDemuxFilters) to High(FSourceDemuxFilters) do
    with FSourceDemuxFilters[Loop] do
    begin
      FFilterPushSource         := nil;
      FFilterMPEG2Demultiplexer := nil;
      FVideoPin                 := nil;
      FAudioPin                 := nil;
      FAudioAc3Pin              := nil;
      FActiveAudioPid           := -1;
      FActiveVideoPid           := -1;
      FActiveAudioAc3           := False;
    end;
  FSourceDemuxDefault   := Low(FSourceDemuxFilters);
  FVideoWindow          := nil;
  FWindowlessControl    := nil;
  ZeroMemory(@FAlphaBitmap7, sizeof(FAlphaBitmap7));
  ZeroMemory(@FAlphaBitmap7, sizeof(FAlphaBitmap9));
  FAlphaBitmap7.Hdc := 0;
  FAlphaBitmap9.Hdc := 0;
end;

{------------------------------------------------------------------------------
  Params  : <Bitmap>    Bitmap to blend in
            <Dest>      Source position/size
            <Blend>     Blending value (0.0...1.0 == transparent..opaque)
            <ColorKey>  Transparancy color
  Returns : <Result>  True if success

  Descript: Blend in application image
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.BlendApplicationImage(Bitmap: HDC; Src: TRect; Blend: Single; ColorKey: COLORREF): Boolean;
begin
  Result := True;
  if not Assigned(FFilterVmr) then
    Exit;
  case (FOptions and CDirectShowMethodVmr) of
    CDirectShowMethodVideoRenderer: ;
    CDirectShowMethodVideoRenderer7:
      begin
        with FAlphaBitmap7 do
        begin
          Hdc          := Bitmap;
          rSrc         := Src;
          rDest.Left   := 0;
          rDest.Top    := 0;
          rDest.Right  := 1;
          rDest.Bottom := 1;
          dwFlags      := VMRBITMAP_SRCCOLORKEY or VMRBITMAP_HDC;
          clrSrcKey    := ColorKey;
          fAlpha       := Blend;
        end;
        (FFilterVmr as IVMRMixerBitmap).SetAlphaBitmap(FAlphaBitmap7);
      end;
    CDirectShowMethodVideoRenderer9:
      begin
        with FAlphaBitmap9 do
        begin
          Hdc          := Bitmap;
          rSrc         := Src;
          rDest.Left   := 0;
          rDest.Top    := 0;
          rDest.Right  := 1;
          rDest.Bottom := 1;
          dwFlags      := VMRBITMAP_SRCCOLORKEY or VMRBITMAP_HDC;
          clrSrcKey    := ColorKey;
          fAlpha       := Blend;
        end;
        (FFilterVmr as IVMRMixerBitmap9).SetAlphaBitmap(@FAlphaBitmap9);
      end;
  end;
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>  True if 'windowless' mode

  Descript: Get windowless mode (VMR)
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.DirectShowWindowless: Boolean;
begin
  if Assigned(DirectShowGraph) and Assigned(FWindowlessControl) then
    Result := True
  else
    Result := False;
end;

{------------------------------------------------------------------------------
  Params  : <Index>  Filter index (0 = first)
  Returns : <Result> Name of filter (empty if no filter at index)

  Descript: Get filter name
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.DirectShowGetFilterName(Index: Integer): string;
var
  EnumFilters: IEnumFilters;
  BaseFilter : IBaseFilter;
  FilterInfo : TFilterInfo;
  FilterIndex: Integer;
begin
  Result := '';
  try
    FFilterGraphStreamIn.EnumFilters(EnumFilters);
    FilterIndex := -1;
    EnumFilters.Reset;
    while (EnumFilters.Next(1, BaseFilter, nil) = S_OK) and (FilterIndex < Index) do
    begin
      BaseFilter.QueryFilterInfo(FilterInfo);
      Result := FilterInfo.achName;
      Inc(FilterIndex);
    end;
    // Make sure an incorrect index returns nothing
    if FilterIndex <> Index then
      Result := '';
  except
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Parent>      Handle of parent to create property page in
            <FilterName>  Name of filter
            <QueryOnly>   True if only to check for property page (not to show)
  Returns : <Result>      True if property page available

  Descript: Show property page of filter
  Notes   :
 ------------------------------------------------------------------------------}

function TDirectShowGraph.DirectShowShowPropertyPage(Parent: THandle; FilterName: WideString; QueryOnly: Boolean): Boolean;
var
  BaseFilter          : IBaseFilter;
//  SpecifyPropertyPages: ISpecifyPropertyPages;
  CAGUID              : TCAGUID;
  FilterInfo          : TFilterInfo;
  Hr                  : HRESULT;
begin
  Result := False;
  try
    // Get filter using the name
    Hr := FFilterGraphStreamIn.FindFilterByName(PWideChar(FilterName), BaseFilter);
    if Succeeded(Hr) then
    begin
      //    ZeroMemory(@FilterInfo, SizeOf(TFilterInfo));
      // Get property pages
//      Hr := BaseFilter.QueryInterface(IID_ISpecifyPropertyPages, SpecifyPropertyPages);
//      if Succeeded(Hr) then
      begin
        // Get GUID
//        Hr := SpecifyPropertyPages.GetPages(CAGUID);
        Hr := (BaseFilter as ISpecifyPropertyPages).GetPages(CAGUID);
        if Succeeded(Hr) then
        begin
          // Check number of pages available
          if CAGUID.cElems < 1 then
            Exit;
          if not QueryOnly then
          begin
            // Get info
            Hr := BaseFilter.QueryFilterInfo(FilterInfo);
            if Succeeded(Hr) then
            begin
              Hr := OleCreatePropertyFrame(Parent, 0, 0, FilterInfo.achName, 1, @BaseFilter, CAGUID.cElems, CAGUID.pElems, 0, 0, nil);
              if Succeeded(Hr) then
                Result := True;
            end;
          end
          else
            Result := True;
        end;
      end;
    end;
  except
  end;
end;

{------------------------------------------------------------------------------
  Params  : <GraphFile>  Filename (without an extension)
  Returns : -

  Descript: Save current graph to file
  Notes   : Because there can be two graphs active (StreamBufferSink/Source)
            two files might need to be created with a different name.
 ------------------------------------------------------------------------------}

function TDirectShowGraph.DirectShowSaveGraph(GraphFile: AnsiString): Boolean;
begin
  Result := False;
  if Assigned(FFilterGraphStreamIn) then
  begin
//    if FFilterGraphStreamIn <> FFilterGraphStreamOut then
//    begin
//      SaveGraphFile(FFilterGraphStreamIn, GraphFile + '_SINK.GRF');
//      Result := SaveGraphFile(FFilterGraphStreamOut,       GraphFile + '_SOURCE.GRF');
//    end
//    else
      Result := SaveGraphFile(FFilterGraphStreamIn, GraphFile + '.GRF')
  end;
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Stop DirectShow interface by releasing all assigned sruff
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowStop;
var
  Loop: Integer;
begin
  if Assigned(FFilterGraphStreamIn) then
  begin
    try
      (FFilterGraphStreamIn as IMediaControl).Stop;
      FFilterGraphStreamIn.Abort;
      if FRotEntry[0] <> -1 then
        RemoveFromRot(FRotEntry[0]);
      if FRotEntry[1] <> -1 then
        RemoveFromRot(FRotEntry[1]);
      // Important to stop the message drain, otherwise errors occur
      FRotEntry[0] := -1;
      FRotEntry[1] := -1;
      FVideoWindow := nil;
      FFilterGraphStreamIn  := nil;
//      FFilterStreamBufferSource := nil;
//      FFilterStreamBufferSink   := nil;
      FFilterVmr            := nil;
      FBasicVideo           := nil;
      for Loop := Low(FSourceDemuxFilters) to High(FSourceDemuxFilters) do
        with FSourceDemuxFilters[Loop] do
        begin
          FFilterPushSource         := nil;
          FFilterMPEG2Demultiplexer := nil;
          FVideoPin                 := nil;
          FAudioPin                 := nil;
          FAudioAc3Pin              := nil;
          FActiveAudioPid           := -1;
          FActiveVideoPid           := -1;
        end;
      FSourceDemuxDefault := Low(FSourceDemuxFilters);
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer7: IVMRWindowlessControl(FWindowlessControl)  := nil;
        CDirectShowMethodVideoRenderer9: IVMRWindowlessControl9(FWindowlessControl) := nil;
      end;
      if FLogFile <> INVALID_HANDLE_VALUE then
        CloseHandle(FLogFile);
      FLogFile := INVALID_HANDLE_VALUE;
    except
    end;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <FullScreen>  True to set full screen mode
                          False to restore original mode
  Returns : -

  Descript: Set full screen mode
  Notes   : Only for non-VMR9 modes
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowFullScreenMode(FullScreen: Boolean);
begin
  try
    if Assigned(FVideoWindow) then
      FVideoWindow.put_FullScreenMode(FullScreen);
  except
    DirectShowStop;
    ShowMessage('DirectShow FullScreenMode error');
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Video>  Video window
  Returns : -

  Descript: Set video window
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowSetVideo(Video: HWND);
begin
  try
    if (FOptions and CDirectShowMethodVmrSpecials) <> CDirectShowMethodVideoRendererWindowless then
      Exit;
    if not Assigned(FWindowlessControl) then
      Exit;
    FOwner := Video;
    case (FOptions and CDirectShowMethodVmr) of
      CDirectShowMethodVideoRenderer7: IVMRWindowlessControl(FWindowlessControl).SetVideoClippingWindow(FOwner);
      CDirectShowMethodVideoRenderer9: IVMRWindowlessControl9(FWindowlessControl).SetVideoClippingWindow(FOwner);
    end;
  except
    DirectShowStop;
    ShowMessage('DirectShow SetVideo error');
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Destination>  Destination rectangle
  Returns : -

  Descript: Resize to rectangle area
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowResizeRectangle(Destination: TRect);
begin
  try
    if ((FOptions and CDirectShowMethodVmrSpecials) = CDirectShowMethodVideoRendererWindowless) and (Assigned(FWindowlessControl)) then
    begin
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer : if Assigned(FVideoWindow) then
          FVideoWindow.SetWindowPosition(Destination.Left, Destination.Top, Destination.Right - Destination.Left, Destination.Bottom - Destination.Top);
        CDirectShowMethodVideoRenderer7: IVMRWindowlessControl(FWindowlessControl).SetVideoPosition(nil, @Destination);
        CDirectShowMethodVideoRenderer9: IVMRWindowlessControl9(FWindowlessControl).SetVideoPosition(nil, @Destination);
      end;
    end
    else if Assigned(FVideoWindow) then
      FVideoWindow.SetWindowPosition(Destination.Left, Destination.Top, Destination.Right - Destination.Left, Destination.Bottom - Destination.Top);
  except
    DirectShowStop;
    ShowMessage('DirectShow Resize error');
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Crop>  Crop adjustments
                    [0, 0, 0, 0]     resets to native size
                    [-1, -1, -1, -1] resets to native size plus resets aspect ratio
  Returns : -

  Descript: Resize the source rectangle area
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowSourceCrop(Crop: TRect);
var
  SrcRect : TRect;
  DestRect: TRect;
  Left    : LongInt;
  Top     : LongInt;
  Width   : LongInt;
  Height  : LongInt;
  AWidth  : LongInt;
  AHeight : LongInt;
  Special : Boolean;
begin
  try
    if ((FOptions and CDirectShowMethodVmrSpecials) = CDirectShowMethodVideoRendererWindowless) and (Assigned(FWindowlessControl)) then
    begin
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer7:
          begin
            IVMRWindowlessControl(FWindowlessControl).SetAspectRatioMode(VMR_ARMODE_NONE);
            IVMRWindowlessControl(FWindowlessControl).GetVideoPosition(SrcRect, DestRect);
            SrcRect.Left   := SrcRect.Left   + Crop.Left;
            SrcRect.Right  := SrcRect.Right  - Crop.Right;
            SrcRect.Top    := SrcRect.Top    + Crop.Top;
            SrcRect.Bottom := SrcRect.Bottom - Crop.Bottom;
            Special := False;
            if (Crop.Left = Crop.Right) and (Crop.Left = Crop.Top) and (Crop.Left = Crop.Bottom) then
            begin
              If Crop.Left = 0 then
                Special := True;
              If Crop.Left = -1 then
                Special := True;
            end;
            if Special then
            begin
              IVMRWindowlessControl(FWindowlessControl).GetNativeVideoSize(Width, Height, Awidth, AHeight);
              if Crop.Left = -1 then
                IVMRWindowlessControl(FWindowlessControl).SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
              SrcRect.Left   := 0;
              SrcRect.Right  := Width;
              SrcRect.Top    := 0;
              SrcRect.Bottom := Height;
            end;
            IVMRWindowlessControl(FWindowlessControl).SetVideoPosition(@SrcRect, nil);
          end;
        CDirectShowMethodVideoRenderer9:
          begin
            IVMRWindowlessControl9(FWindowlessControl).SetAspectRatioMode(VMR9ARMode_None);
            IVMRWindowlessControl9(FWindowlessControl).GetVideoPosition(SrcRect, DestRect);
            SrcRect.Left   := SrcRect.Left   + Crop.Left;
            SrcRect.Right  := SrcRect.Right  - Crop.Right;
            SrcRect.Top    := SrcRect.Top    + Crop.Top;
            SrcRect.Bottom := SrcRect.Bottom - Crop.Bottom;
            Special := False;
            if (Crop.Left = Crop.Right) and (Crop.Left = Crop.Top) and (Crop.Left = Crop.Bottom) then
            begin
              If Crop.Left = 0 then
                Special := True;
              If Crop.Left = -1 then
                Special := True;
            end;
            if Special then
            begin
              IVMRWindowlessControl(FWindowlessControl).GetNativeVideoSize(Width, Height, Awidth, AHeight);
              if Crop.Left = -1 then
                IVMRWindowlessControl9(FWindowlessControl).SetAspectRatioMode(VMR9ARMode_LetterBox);
              SrcRect.Left   := 0;
              SrcRect.Right  := Width;
              SrcRect.Top    := 0;
              SrcRect.Bottom := Height;
            end;
            IVMRWindowlessControl9(FWindowlessControl).SetVideoPosition(@SrcRect, nil);
          end;
      end;
    end
    else
      if Assigned(FBasicVideo) then
      begin
        FBasicVideo.GetSourcePosition(Left, Top, Width, Height);
        SrcRect.Left   := Left         + Crop.Left;
        SrcRect.Right  := Left + Width - Crop.Right;
        SrcRect.Top    := Top          + Crop.Top;
        SrcRect.Bottom := Top + Height - Crop.Bottom;
        Special := False;
        if (Crop.Left = Crop.Right) and (Crop.Left = Crop.Top) and (Crop.Left = Crop.Bottom) then
        begin
          If Crop.Left = 0 then
            Special := True;
          If Crop.Left = -1 then
            Special := True;
        end;
        if Special then
        begin
          FBasicVideo.GetVideoSize(Width, Height);
          SrcRect.Left   := 0;
          SrcRect.Right  := Width;
          SrcRect.Top    := 0;
          SrcRect.Bottom := Height;
        end;
        FBasicVideo.SetSourcePosition(SrcRect.Left, SrcRect.Top, SrcRect.Right-SrcRect.Left, SrcRect.Bottom-SrcRect.Top);
      end;
  except
    DirectShowStop;
    ShowMessage('DirectShow Resize error');
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Crop>  Crop adjustments
                    [0, 0, 0, 0]     resets to windows size
                    [-1, -1, -1, -1] resets to windows size plus resets aspect ratio
  Returns : -

  Descript: Resize the destination rectangle area
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowDestinationCrop(Crop: TRect);
var
  SrcRect : TRect;
  DestRect: TRect;
  Special : Boolean;
  Left    : LongInt;
  Top     : LongInt;
  Width   : LongInt;
  Height  : LongInt;
begin
  try
    if ((FOptions and CDirectShowMethodVmrSpecials) = CDirectShowMethodVideoRendererWindowless) and (Assigned(FWindowlessControl)) then
    begin
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer7:
          begin
            IVMRWindowlessControl(FWindowlessControl).SetAspectRatioMode(VMR_ARMODE_NONE);
            IVMRWindowlessControl(FWindowlessControl).GetVideoPosition(SrcRect, DestRect);
            DestRect.Left   := DestRect.Left   + Crop.Left;
            DestRect.Right  := DestRect.Right  - Crop.Right;
            DestRect.Top    := DestRect.Top    + Crop.Top;
            DestRect.Bottom := DestRect.Bottom - Crop.Bottom;
            Special := False;
            if (Crop.Left = Crop.Right) and (Crop.Left = Crop.Top) and (Crop.Left = Crop.Bottom) then
            begin
              If Crop.Left = 0 then
                Special := True;
              If Crop.Left = -1 then
                Special := True;
            end;
            if Special then
            begin
              if Crop.Left = -1 then
                IVMRWindowlessControl(FWindowlessControl).SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
              GetWindowRect(FOwner, DestRect);
              DestRect.Left := 0;
              DestRect.Top  := 0;
            end;
            IVMRWindowlessControl(FWindowlessControl).SetVideoPosition(nil, @DestRect);
          end;
        CDirectShowMethodVideoRenderer9:
          begin
            IVMRWindowlessControl9(FWindowlessControl).SetAspectRatioMode(VMR9ARMode_None);
            IVMRWindowlessControl9(FWindowlessControl).GetVideoPosition(SrcRect, DestRect);
            DestRect.Left   := DestRect.Left   + Crop.Left;
            DestRect.Right  := DestRect.Right  - Crop.Right;
            DestRect.Top    := DestRect.Top    + Crop.Top;
            DestRect.Bottom := DestRect.Bottom - Crop.Bottom;
            Special := False;
            if (Crop.Left = Crop.Right) and (Crop.Left = Crop.Top) and (Crop.Left = Crop.Bottom) then
            begin
              If Crop.Left = 0 then
                Special := True;
              If Crop.Left = -1 then
                Special := True;
            end;
            if Special then
            begin
              if Crop.Left = -1 then
                IVMRWindowlessControl(FWindowlessControl).SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
              GetWindowRect(FOwner, DestRect);
              DestRect.Left := 0;
              DestRect.Top  := 0;
            end;
            IVMRWindowlessControl9(FWindowlessControl).SetVideoPosition(nil, @DestRect);
          end;
      end;
    end
    else
      if Assigned(FBasicVideo) then
      begin
        FBasicVideo.GetDestinationPosition(Left, Top, Width, Height);
        DestRect.Left   := Left          + Crop.Left;
        DestRect.Right  := Left + Width  - Crop.Right;
        DestRect.Top    := Top           + Crop.Top;
        DestRect.Bottom := Top  + Height - Crop.Bottom;
        Special := False;
        if (Crop.Left = Crop.Right) and (Crop.Left = Crop.Top) and (Crop.Left = Crop.Bottom) then
        begin
          If Crop.Left = 0 then
            Special := True;
          If Crop.Left = -1 then
            Special := True;
        end;
        if Special then
        begin
          GetWindowRect(FOwner, DestRect);
          DestRect.Left := 0;
          DestRect.Top  := 0;
        end;
        FBasicVideo.SetDestinationPosition(DestRect.Left, DestRect.Top, DestRect.Right-DestRect.Left, DestRect.Bottom-DestRect.Top);
      end;
  except
    DirectShowStop;
    ShowMessage('DirectShow Resize error');
  end;
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Resize to full client area
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowResize;
var
  DestinationRect: TRect;
begin
  try
    GetWindowRect(FOwner, DestinationRect);
    DestinationRect.Left := 0;
    DestinationRect.Top  := 0;
    DirectShowResizeRectangle(DestinationRect);
  except
    DirectShowStop;
    ShowMessage('DirectShow Resize error');
  end;
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : <Width>  Width of native video
            <Height> Height of native video

  Descript: Get native video size
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowGetSize(var Width: Integer; var Height:
  Integer);
var
  AWidth: Integer;
  AHeight: Integer;
  //  MediaType  : AM_MEDIA_TYPE;
  //  VideoHeader: PVideoInfoHeader;
begin
  try
    //    // Next could be used instead, but needs the correct PIN of the video decoder
    //    XXXXXXPin.ConnectionMediaType(MediaType);
    //    if IsEqualGUID(MediaType.majortype, MEDIATYPE_Video) then
    //    begin
    //      // Get pointer to header info
    //      VideoHeader := PVideoInfoHeader(MediaType.pbFormat);
    //      Width  := VideoHeader.bmiHeader.biWidth;
    //      Height := VideoHeader.bmiHeader.biHeight;
    //    end;
    if ((FOptions and CDirectShowMethodVmrSpecials) = CDirectShowMethodVideoRendererWindowless) and
       (Assigned(FWindowlessControl)) then
    begin
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer : if Assigned(FBasicVideo) then
                                           FBasicVideo.GetVideoSize(Width, Height);
        CDirectShowMethodVideoRenderer7: IVMRWindowlessControl(FWindowlessControl).GetNativeVideoSize (Width, Height, AWidth, AHeight);
        CDirectShowMethodVideoRenderer9: IVMRWindowlessControl9(FWindowlessControl).GetNativeVideoSize(Width, Height, AWidth, AHeight);
      end;
    end
    else if Assigned(FBasicVideo) then
      FBasicVideo.GetVideoSize(Width, Height)
  except
    DirectShowStop;
    ShowMessage('DirectShow GetSize error');
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Dib>     Pointer to DIB
  Returns : <Result>  Size of header

  Descript: Get header size of DIB
  Notes   :
 ------------------------------------------------------------------------------}

function GetDibHeaderSize(Dib: PBitmapInfo): Integer;
begin
  // Defaults for colors used, so get the color table count from
  // the number of bits per pixel
  if (Dib.bmiHeader.biClrUsed = 0) then
  begin
    case Dib.bmiHeader.biBitCount of
      1: Dib.bmiHeader.biClrUsed := 2;
      2: Dib.bmiHeader.biClrUsed := 4;
      3: Dib.bmiHeader.biClrUsed := 8;
      4: Dib.bmiHeader.biClrUsed := 16;
      5: Dib.bmiHeader.biClrUsed := 32;
      6: Dib.bmiHeader.biClrUsed := 64;
      7: Dib.bmiHeader.biClrUsed := 128;
      8: Dib.bmiHeader.biClrUsed := 256;
    end;
  end;
  Result := Dib.bmiHeader.biSize + (Dib.bmiHeader.biClrUsed * 4);
  if ((Dib.bmiHeader.biCompression = BI_BITFIELDS) and (Dib.bmiHeader.biSize = SizeOf(TBitmapInfoHeader))) then
    Result := Result + 12;
end;

{------------------------------------------------------------------------------
  Params  : <Width>   Width in pixels
            <Depth>   Depth in pixels
  Returns : <Result>  Size of header

  Descript: Return 4 byte aligned data
  Notes   :
 ------------------------------------------------------------------------------}

function AlignScan(Width: Dword; Depth: Dword): Dword;
begin
  Result := (((Width * Depth) + $1F) and $FFFFFFE0) div 8;
end;

{------------------------------------------------------------------------------
  Params  : <Dib>     Pointer to DIB
  Returns : <Result>  Size of data

  Descript: Return DIB data size
  Notes   :
 ------------------------------------------------------------------------------}

function GetDibDataSize(Dib: PBitmapInfo): Integer;
begin
  case Dib.bmiHeader.biCompression of
    BI_RGB, BI_BITFIELDS:
      begin
        Result := Integer(AlignScan(Dword(Dib.bmiHeader.biWidth), Dword(Dib.bmiHeader.biBitCount))) * Abs(Dib.bmiHeader.biHeight);
        Dib.bmiHeader.biSizeImage := Result;
      end;
    else Result := Dib.bmiHeader.biSizeImage;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Dib>     Pointer to DIB
  Returns : <Result>  Handle to bitmap (0 if none)

  Descript: Convert packed DIB into DIB section (handle)
  Notes   : The caller should release the handle (e.g. with DeleteObject(x)!
 ------------------------------------------------------------------------------}

function PackedDibToDibSection(Dib: PBitmapInfo): HBITMAP;
var
  BmHead   : TBitmapInfoHeader;
  HeadSize : Integer;
  DataSize : Integer;
  DataBits : Pointer;
begin
  CopyMemory(@BmHead, Dib, SizeOf(Bmhead));
  // Get header and data size
  HeadSize := GetDibHeaderSize(@BmHead);
  DataSize := GetDibDataSize(@BmHead);
  // Create DibSection based on header
  Result := CreateDibSection(0, Dib^, DIB_RGB_COLORS, DataBits, 0, 0);
  // Copy bitmap to it
  if Result <> 0 then
    CopyMemory(DataBits, Pointer(@PByteArray(Dib)^[HeadSize]), DataSize);
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>  Handle to bitmap (0 if none)

  Descript: Get current image
  Notes   : The caller must release the handle with DeleteObject(x)!
 ------------------------------------------------------------------------------}

function TDirectShowGraph.DirectShowGetImage: HBITMAP;
var
  Local       : PBitmapInfo;
  FilterState : TFilterState;
  Hr          : HRESULT;

  procedure BasicVideoGetImage;
  var
    ASize  : Integer;
  begin
    if Assigned(FBasicVideo) then
    begin
      (FFilterGraphStreamIn as IMediaControl).Pause;
      (FFilterGraphStreamIn as IMediaControl).GetState(1, FilterState);
      // Check required memory
      ASize := 0;
      Local := nil;
      Hr := FBasicVideo.GetCurrentImage(ASize, Local^);
      if Succeeded(Hr) then
      begin
        GetMem(Local, ASize);
        try
          Hr := FBasicVideo.GetCurrentImage(ASize, Local^);
          Result := PackedDibToDibSection(Local);
        finally
          FreeMem(Local);
        end;
      end;
      (FFilterGraphStreamIn as IMediaControl).Run;
    end;
  end;
begin
  Result := 0;
  try
    if ((FOptions and CDirectShowMethodVmrSpecials) =
      CDirectShowMethodVideoRendererWindowless) and
      (Assigned(FWindowlessControl)) then
    begin
      case (FOptions and CDirectShowMethodVmr) of
        CDirectShowMethodVideoRenderer7:
          begin
            Hr := IVMRWindowlessControl(FWindowlessControl).GetCurrentImage(Local);
            if Succeeded(Hr) then
            begin
              Result := PackedDibToDibSection(Local);
              CoTaskMemFree(Local);
            end;
          end;
        CDirectShowMethodVideoRenderer9:
          begin
            Hr := IVMRWindowlessControl9(FWindowlessControl).GetCurrentImage(PByte(Local));
            if Succeeded(Hr) then
            begin
              Result := PackedDibToDibSection(Local);
              CoTaskMemFree(Local);
            end;
          end;
        else BasicVideoGetImage;
      end;
    end
    else
      BasicVideoGetImage;
  except
    ShowMessage('DirectShow GetCurrentImage error');
    Result := 0;
  end;
end;

{------------------------------------------------------------------------------
  Descript: DirectShow MPEG2 demultipexer filter
 ------------------------------------------------------------------------------}
{------------------------------------------------------------------------------
  Params  : <Device>    device to address
            <VideoPid>  Video PID
            <AudioPid>  Audio PID
            <AudioAC3>  True if AC3 pin to use
  Returns : -

  Descript: Set new PIDs to use for MPEG2 demultiplexer
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowMpeg2DemultiplexerSetNewPids(Device: Integer; VideoPid: Integer; AudioPid: Integer; AudioAC3: Boolean);
var
  MapPid: Cardinal;
begin
  try
    if (Device < Low(FSourceDemuxFilters)) or (Device > High(FSourceDemuxFilters)) then
      Device := FSourceDemuxDefault;
    if not Assigned(FSourceDemuxFilters[Device].FVideoPin) then
      Device := FSourceDemuxDefault;
    if not Assigned(FSourceDemuxFilters[Device].FVideoPin) then
      Exit;
    with FSourceDemuxFilters[Device] do
    begin
      if not Assigned(FAudioPin) and not Assigned(FAudioAc3Pin) then
        Exit;
      if FActiveVideoPid >= 0 then
      begin
        MapPid := FActiveVideoPid;
        (FVideoPin as IMPEG2PIDMap).UnmapPID(1, @MapPid);
      end;
      if FActiveAudioPid >= 0 then
      begin
        MapPid := FActiveAudioPid;
        if FActiveAudioAc3 then
          (FAudioAc3Pin as IMPEG2PIDMap).UnmapPID(1, @MapPid)
        else
          (FAudioPin as IMPEG2PIDMap).UnmapPID(1, @MapPid);
      end;

      // AC3 only if pin is available and AC3 output is required
      if Assigned(FAudioAc3Pin) and AudioAc3 then
        FActiveAudioAc3 := True
      else
        FActiveAudioAc3 := False;

      // Set new active PIDs
      FActiveVideoPid := VideoPid;
      FActiveAudioPid := AudioPid;

      if FActiveVideoPid >= 0 then
      begin
        MapPid := FActiveVideoPid;
        (FVideoPin as IMPEG2PIDMap).MapPID(1, @MapPid, MEDIA_ELEMENTARY_STREAM);
      end;
      if FActiveAudioPid >= 0 then
      begin
        MapPid := FActiveAudioPid;
        if not FActiveAudioAc3 then
          (FAudioPin as IMPEG2PIDMap).MapPID(1, @MapPid, MEDIA_ELEMENTARY_STREAM)
        else
          (FAudioAc3Pin as IMPEG2PIDMap).MapPID(1, @MapPid, MEDIA_ELEMENTARY_STREAM);
      end;
    end;
  except
    DirectShowStop;
    ShowMessage('DirectShow Mpeg2DemultiplexerSetNewPids error');
  end;
end;

{------------------------------------------------------------------------------
  Descript: DirectShow Push Source filter
 ------------------------------------------------------------------------------}
{------------------------------------------------------------------------------
  Params  : <Device>        Device
            <Buffer>        Pointer to data
            <BufferLength>  Length of valid data in buffer
  Returns : -

  Descript: Send data to push source filter
  Notes   :
 ------------------------------------------------------------------------------}

procedure TDirectShowGraph.DirectShowPushSourceSendData(Device: Integer; Buffer: PByte; BufferLength: Integer);
var
  Delivered: Integer;
begin
  try
    if (Device < Low(FSourceDemuxFilters)) or (Device > High(FSourceDemuxFilters)) then
      Device := FSourceDemuxDefault;
    if not Assigned(FSourceDemuxFilters[Device].FFilterPushSource) then
      Device := FSourceDemuxDefault;
    if not Assigned(FSourceDemuxFilters[Device].FFilterPushSource) then
      Exit;
    FSourceDemuxFilters[Device].FFilterPushSourceI.PushData(Buffer, BufferLength, Delivered);
  except
    DirectShowStop;
    ShowMessage('DirectShow push source PushData error (reintialize manually)');
  end;
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Initialization
  Notes   :
 ------------------------------------------------------------------------------}

procedure InitializeUnit;
begin
  CoInitialize(nil);
  DirectShowGraph := TDirectShowGraph.Create(0);
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Finalization
  Notes   :
 ------------------------------------------------------------------------------}

procedure FinalizeUnit;
begin
  if Assigned(DirectShowGraph) then
    FreeAndNil(DirectShowGraph);
  CoUnInitialize;
end;

initialization
  InitializeUnit;

finalization
  FinalizeUnit;
end.

