{******************************************************************************}
{ FileName............: MajorPushSourceUnit002                                 }
{ Project.............: DirectShow                                             }
{ Author(s)...........: MM                                                     }
{ Version.............: 2.00                                                   }
{------------------------------------------------------------------------------}
{  DirectShow push source filter property page + debug                         }
{                                                                              }
{  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. }
{                                                                              }
{------------------------------------------------------------------------------}
{                                                                              }
{ Version   Date   Comment                                                     }
{  1.00   20060115 - Initial release                                           }
{  2.00   20060922 - UDP port setting added                                    }
{                  - Sync data setting added                                   }
{******************************************************************************}
unit MajorPushSourceUnit002;

interface
uses
  BaseClass,
  Classes,
  Controls,
  ActiveX,
  DirectShow9,
  ExtCtrls,
  Graphics,
  MajorPushSourceUnit001,
  Messages,
  StdCtrls,
  SysUtils,
  MMSystem,
  Windows, Mask, ComCtrls, Dialogs;

const
  CPushedDataSize = 64;

type
  TPushThread = class(TThread)
    FSourceFilter: IMajorPushSource;
    FFileName    : AnsiString;
    FEndOfStream : Boolean;
    FPushDelay   : Integer;
  public
    procedure Execute; override;
  end;

  TfrmProperties = class(TFormPropertyPage)
    Image1             : TImage;
    tmrUpdate          : TTimer;
    pnlInfo            : TPanel;
    Label1             : TLabel;
    lblVersion         : TLabel;
    mmoInformation: TMemo;
    grpMediaType: TGroupBox;
    mskGuidMajor: TMaskEdit;
    mskGuidMinor: TMaskEdit;
    mskGuidFormat: TMaskEdit;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    grpPushedData: TGroupBox;
    lblInformation: TLabel;
    lblBytes: TLabel;
    lblInformation2: TLabel;
    Label2: TLabel;
    btnSetMediaType: TButton;
    grpBuffer: TGroupBox;
    Label6: TLabel;
    Label7: TLabel;
    mskBufferSize: TMaskEdit;
    mskBufferAlignment: TMaskEdit;
    btnSetBufferSize: TButton;
    Label8: TLabel;
    Label9: TLabel;
    btnGetBufferSize: TButton;
    btnGetMediaType: TButton;
    btnFile: TButton;
    dlgOpen: TOpenDialog;
    chkMedia: TCheckBox;
    mmoPushedData: TMemo;
    mskPushSleep: TMaskEdit;
    Label13: TLabel;
    Label12: TLabel;
    Label10: TLabel;
    grpUdp: TGroupBox;
    Label11: TLabel;
    mskUdpPort: TMaskEdit;
    btnSetUdp: TButton;
    btnGetUdp: TButton;
    chkUdpEnable: TCheckBox;
    chkTimestamps: TCheckBox;
    chkSynchronize: TCheckBox;
    chkLogFile: TCheckBox;
    mskUdpBufferSize: TMaskEdit;
    Label14: TLabel;
    Label15: TLabel;
    procedure tmrUpdateTimer(Sender: TObject);
    procedure btnSetBufferSizeClick(Sender: TObject);
    procedure btnGetBufferSizeClick(Sender: TObject);
    procedure btnGetMediaTypeClick(Sender: TObject);
    procedure btnSetMediaTypeClick(Sender: TObject);
    procedure btnLogOnClick(Sender: TObject);
    procedure btnLogOffClick(Sender: TObject);
    procedure btnFileClick(Sender: TObject);
    procedure chkTimestampsClick(Sender: TObject);
    procedure chkSynchronizeClick(Sender: TObject);
    procedure chkLogFileClick(Sender: TObject);
    procedure btnGetUdpClick(Sender: TObject);
    procedure btnSetUdpClick(Sender: TObject);
    procedure Label14Click(Sender: TObject);
  private
    FSourceFilter: IMajorPushSource;
    FLastTime    : Dword;
    FLastCount   : Integer;
    FFirstTime   : Boolean;
    FPushThread  : TPushThread;
    FPushedData  : PByteArray;
  public
    function  OnConnect(Unknown: IUnknown): HRESULT; override;
    function  OnDisconnect: HRESULT; override;
    function  OnApplyChanges: HRESULT; override;
  end;

implementation

{$R *.DFM}


{------------------------------------------------------------------------------
  Params  : <Unknown>
  Returns : <Result>

  Descript: On connect property page.
  Notes   :
 ------------------------------------------------------------------------------}
function TfrmProperties.OnConnect(Unknown: IUnknown): HRESULT;
var
  hr     : HRESULT;
  Version: PChar;
begin
  Version := nil;
  if not Assigned(Unknown) then
  begin
    Result := E_POINTER;
    Exit;
  end;
  // Next will fail if connected to a running graph from the *ROT* ...
  hr := Unknown.QueryInterface(IMajorPushSource, FSourceFilter);
  if (FAILED(hr)) then
  begin
    FSourceFilter := nil;
    Result := E_NOINTERFACE;
    Exit;
  end;
  // Place version information in tab header
  // This will fail if displayed from a running graph, since the Caption is
  // then 'locked'!
  GetMem(Version, 128);
  FSourceFilter.GetVersionInformation(Version);
  Caption := Version;
  FreeMem(Version);
  FFirstTime  := True;
  FPushThread := nil;
  GetMem(FPushedData, CPushedDataSize);

  Result := NOERROR;
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>

  Descript: On disconnect property page.
  Notes   :
 ------------------------------------------------------------------------------}
function TfrmProperties.OnDisconnect: HRESULT;
begin
  if Assigned(FPushThread) then
  begin
    FPushThread.Terminate;
    FPushThread.Resume;
    FPushThread.WaitFor;
    FreeAndnil(FPushThread);
  end;
  FreeMem(FPushedData);
  FPushedData := nil;
  // Disable debug buffering
  if Assigned(FSourceFilter) then
    FSourceFilter.GetPushedData(nil, 0);
  Result := NOERROR;
end;


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

  Descript: On apply changes property page.
  Notes   :
 ------------------------------------------------------------------------------}
function TfrmProperties.OnApplyChanges: HRESULT;
begin
  Result := NOERROR;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>
  Returns : -

  Descript: Update form.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.tmrUpdateTimer(Sender: TObject);
var
  Info      : PChar;
  Data      : Integer;
  TickCount : Dword;
  DeltaTime : Integer;
  DeltaCount: Integer;
  Calc      : Extended;
  AString   : AnsiString;
  Loop      : Integer;
  MaxLoop   : Integer;
  ABool     : Boolean;
begin
  if Assigned(FSourceFilter) then
  begin
    // Update information
    GetMem(Info, 128);
    repeat
      FSourceFilter.GetInformation(Info);
      if Length(Info) > 0 then
        mmoInformation.Lines.Add(Info);
    until (Length(Info) = 0);
    FSourceFilter.GetDeliveredCount(Data);
    TickCount := GetTickCount;
    DeltaTime := TickCount - FLastTime;
    DeltaCount := Data - FLastCount;
    Calc := (DeltaCount / 1000) * DeltaTime;
    if Calc > 10000000 then
      lblInformation2.Caption := '>10000000'
    else
      lblInformation2.Caption := format('%d', [Round(Calc)]);
    lblInformation.Caption  := format('%d', [Data]);
    FLastTime  := TickCount;
    FLastCount := Data;
    // Pushed data display
    if Assigned(FPushedData) then
    begin
      FSourceFilter.GetPushedData(PByte(FPushedData), CPushedDataSize);
      AString := '';
      MaxLoop := CPushedDataSize;
      if MaxLoop > 64 then
        MaxLoop := 64;
      for Loop := 0 to MaxLoop - 1 do
      begin
        if Loop <> 0 then
          AString := AString + ' ';
        AString := AString + format('%2.2x', [FPushedData[Loop]]);
      end;
      mmoPushedData.Text := AString;
    end;
    // First time initialization
    if FFirstTime then
    begin
      FSourceFilter.GetVersionInformation(Info);
      lblVersion.Caption := Info;
      btnGetBufferSizeClick(nil);
      btnGetMediaTypeClick(nil);
      btnGetUdpClick(nil);
      FSourceFilter.GetAddTimestamps(ABool);
      chkTimestamps.Checked := ABool;
      FSourceFilter.GetSyncData(ABool);
      chkSynchronize.Checked := ABool;
      FSourceFilter.GetLog(ABool);
      chkLogFile.Checked := ABool;
      FFirstTime := False;
      tmrUpdate.Interval := 1000;
    end;
    // Check for pushing a file
    if btnFile.Caption <> 'Push file' then
    begin
      if Assigned(FPushThread) then
      begin
        // If pushing the file has been terminated, check for error (not FEndOfStream)
        if FPushThread.Terminated then
        begin
          if not FPushThread.FEndOfStream then
            mmoInformation.Lines.Add('Pushing file data terminated (e.g. no longer a running graph)');
          FreeAndnil(FPushThread);
          btnFile.Caption := 'Push file';
        end;
      end
      else
        btnFile.Caption := 'Push file';
    end;
    FreeMem(Info);
  end
  else
  begin
    lblInformation.Caption := '-';
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Set buffer size.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnSetBufferSizeClick(Sender: TObject);
var
  Size : Integer;
  Align: Integer;
  Error: Integer;
  AText: AnsiString;
begin
  if Assigned(FSourceFilter) then
  begin
    try
      AText := mskBufferSize.Text;
      while (Pos(' ', AText) <> 0) do
        AText[Pos(' ', AText)] := '0';
      Val(AText, Size, Error);
      if (Size > 0) and (Error = 0) then
      begin
        AText := mskBufferAlignment.Text;
        while (Pos(' ', AText) <> 0) do
          AText[Pos(' ', AText)] := '0';
        Val(AText, Align, Error);
        if Error = 0 then
        begin
          FSourceFilter.SetBufferSize(Size, Align);
        end;
      end;
    except
    end;
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Get buffer size.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnGetBufferSizeClick(Sender: TObject);
var
  Size : Integer;
  Align: Integer;
begin
  if Assigned(FSourceFilter) then
  begin
    try
      FSourceFilter.GetBufferSize(Size, Align);
      if Size > 9999999 then
        Size := 9999999;
      if Align > 9999999 then
        Align := 9999999;
      mskBufferSize.Text      := format('%7.7d', [Size]);
      mskBufferAlignment.Text := format('%7.7d', [Align]);
    except
    end;
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Set media types.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnSetMediaTypeClick(Sender: TObject);
var
  MajorType : TCLSID;
  MinorType : TCLSID;
  FormatType: TCLSID;
  AText     : AnsiString;
  Error     : Integer;
begin
  if Assigned(FSourceFilter) then
  begin
    try
      AText := mskGuidMajor.Text;
      while (Pos(' ', AText) <> 0) do
        AText[Pos(' ', AText)] := '0';
      Val('$' + Copy(AText, 1, 8), MajorType.D1, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 10, 4), MajorType.D2, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 15, 4), MajorType.D3, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 20, 2), MajorType.D4[0], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 22, 2), MajorType.D4[1], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 25, 2), MajorType.D4[2], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 27, 2), MajorType.D4[3], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 29, 2), MajorType.D4[4], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 31, 2), MajorType.D4[5], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 33, 2), MajorType.D4[6], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 35, 2), MajorType.D4[7], Error);
      AText := mskGuidMinor.Text;
      while (Pos(' ', AText) <> 0) do
        AText[Pos(' ', AText)] := '0';
      Val('$' + Copy(AText, 1, 8), MinorType.D1, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 10, 4), MinorType.D2, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 15, 4), MinorType.D3, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 20, 2), MinorType.D4[0], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 22, 2), MinorType.D4[1], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 25, 2), MinorType.D4[2], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 27, 2), MinorType.D4[3], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 29, 2), MinorType.D4[4], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 31, 2), MinorType.D4[5], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 33, 2), MinorType.D4[6], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 35, 2), MinorType.D4[7], Error);
      AText := mskGuidFormat.Text;
      while (Pos(' ', AText) <> 0) do
        AText[Pos(' ', AText)] := '0';
      Val('$' + Copy(AText, 1, 8), FormatType.D1, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 10, 4), FormatType.D2, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 15, 4), FormatType.D3, Error);
      if Error = 0 then
        Val('$' + Copy(AText, 20, 2), FormatType.D4[0], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 22, 2), FormatType.D4[1], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 25, 2), FormatType.D4[2], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 27, 2), FormatType.D4[3], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 29, 2), FormatType.D4[4], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 31, 2), FormatType.D4[5], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 33, 2), FormatType.D4[6], Error);
      if Error = 0 then
        Val('$' + Copy(AText, 35, 2), FormatType.D4[7], Error);
      if Error = 0 then
        FSourceFilter.SetMediaType(@MajorType, @MinorType, @FormatType, chkMedia.Checked);
    except
    end;
  end;

end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Get media types.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnGetMediaTypeClick(Sender: TObject);
var
  MajorType : TCLSID;
  MinorType : TCLSID;
  FormatType: TCLSID;
  CheckType : Boolean;
begin
  if Assigned(FSourceFilter) then
  begin
    try
      FSourceFilter.GetMediaType(@MajorType, @MinorType, @FormatType, CheckType);
      mskGuidMajor.Text := format('%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x',
      [MajorType.D1,    MajorType.D2,    MajorType.D3,
       MajorType.D4[0], MajorType.D4[1],
       MajorType.D4[2], MajorType.D4[3], MajorType.D4[4],
       MajorType.D4[5], MajorType.D4[6], MajorType.D4[7]]);
      mskGuidMinor.Text := format('%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x',
      [MinorType.D1,    MinorType.D2,    MinorType.D3,
       MinorType.D4[0], MinorType.D4[1],
       MinorType.D4[2], MinorType.D4[3], MinorType.D4[4],
       MinorType.D4[5], MinorType.D4[6], MinorType.D4[7]]);
      mskGuidFormat.Text := format('%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x',
      [FormatType.D1,    FormatType.D2,    FormatType.D3,
       FormatType.D4[0], FormatType.D4[1],
       FormatType.D4[2], FormatType.D4[3], FormatType.D4[4],
       FormatType.D4[5], FormatType.D4[6], FormatType.D4[7]]);
      chkMedia.Checked := CheckType;
    except
    end;
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Get UDP port.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnGetUdpClick(Sender: TObject);
var
  Port      : Integer;
  BufferSize: Integer;
begin
  if Assigned(FSourceFilter) then
    try
      FSourceFilter.GetUdp(Port, BufferSize);
      chkUdpEnable.Checked := (Port > 0);
      mskUdpPort.Text := format('%5.5d', [Abs(Port)]);
      mskUdpBufferSize.Text := format('%7.7d', [Abs(BufferSize)]);
    except
    end;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Set UDP port.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnSetUdpClick(Sender: TObject);
var
  Port      : Integer;
  BufferSize: Integer;
  Error     : Integer;
  AText     : AnsiString;
begin
  if Assigned(FSourceFilter) then
  begin
    try
      AText := mskUdpPort.Text;
      while (Pos(' ', AText) <> 0) do
        AText[Pos(' ', AText)] := '0';
      Val(AText, Port, Error);
      if not chkUdpEnable.Checked then
        Port := -Port;
      AText := mskUdpBufferSize.Text;
      while (Pos(' ', AText) <> 0) do
        AText[Pos(' ', AText)] := '0';
      Val(AText, BufferSize, Error);
      if (Error = 0) then
        FSourceFilter.SetUdp(Port, BufferSize);
    except
    end;
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Enable/disable logging to file.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.chkLogFileClick(Sender: TObject);
begin
  if Assigned(FSourceFilter) then
    FSourceFilter.SetLog(chkLogFile.Checked);
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Enable/disable synchronization.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.chkSynchronizeClick(Sender: TObject);
begin
  if Assigned(FSourceFilter) then
    FSourceFilter.SetSyncData(chkSynchronize.Checked);
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Enable/disable timestamps.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.chkTimestampsClick(Sender: TObject);
begin
  if Assigned(FSourceFilter) then
    FSourceFilter.SetAddTimestamps(chkTimestamps.Checked);
end;


procedure TfrmProperties.Label14Click(Sender: TObject);
begin

end;

{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Enable logfile.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnLogOnClick(Sender: TObject);
begin
  if Assigned(FSourceFilter) then
    FSourceFilter.SetLog(True);
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Disable log file.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnLogOffClick(Sender: TObject);
begin
  if Assigned(FSourceFilter) then
    FSourceFilter.SetLog(False);
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Push data from a file.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmProperties.btnFileClick(Sender: TObject);
var
  AText: AnsiString;
  Delay: Integer;
  Error: Integer;
begin
  if Assigned(FSourceFilter) then
  begin
    if Assigned(FPushThread) then
    begin
      FPushThread.FEndOfStream := True;
      FPushThread.Terminate;
      // Timer handles destruction
    end
    else
    begin
      if dlgOpen.Execute then
      begin
        FPushThread := TPushThread.Create(True);
        FPushThread.FSourceFilter := FSourceFilter;
        FPushThread.FFileName     := dlgOpen.FileName;
        AText := mskPushSleep.Text;
        while (Pos(' ', AText) <> 0) do
          AText[Pos(' ', AText)] := '0';
        Val(AText, Delay, Error);
        FPushThread.FPushDelay    := Delay;
        FPushThread.Resume;
        btnFile.Caption := 'Stop push';
        mmoInformation.Lines.Add(format('Starting pushing file with %d ms delay between buffers', [Delay]));
      end;
    end
  end;
end;

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

  Descript: Push data from file (thread).
  Notes   : Only has some real effect when the graph is running!
 ------------------------------------------------------------------------------}
procedure TPushThread.Execute;
var
  FileStream     : TFilestream;
  FileBuffer     : Pointer;
  FileBufferSize : Integer;
  BufferAlignment: Integer;
  DataRead       : Longint;
  DataDelivered  : Integer;
  TimerCaps      : TTimeCaps;
begin
  try
    // Increase resolution for timing
    if timeGetDevCaps(@TimerCaps, SizeOf(TimerCaps)) = TIMERR_NOERROR then
      timeBeginPeriod(TimerCaps.wPeriodMin);
    try
      // Indicate abnormal ending
      FEndOfStream := False;
      // Open file
      FileStream := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyNone);
      if Assigned(FileStream) then
      try
        // Get buffer size to use for pushing
        FSourceFilter.GetBufferSize(FileBufferSize, BufferAlignment);
        // Make sure data will be re-aligned
        FSourceFilter.FlushData;
        // Allocate buffer for file data
        GetMem(FileBuffer, FileBufferSize);
        if Assigned(FileBuffer) then
        try
          // Wait until file pushed or terminated elsewhere
          while not Terminated do
          begin
            // Use Sleep(5) if you want to be to respond to stopping the graph
            // requests (which are otherwise delayed for a very long time).
            Sleep(FPushDelay);
            // Read data from file
            DataRead := FileStream.Read(FileBuffer^, FileBufferSize);
            if DataRead <> 0 then
            begin
              // Push the data but terminate when it could not be delivered
              // which typically happens when no graph is running
              if FSourceFilter.PushData(FileBuffer, DataRead, DataDelivered) <> S_OK then
                Terminate;
            end
            else
            begin
              Terminate;
              // Indicate normal ending
              FEndOfStream := True;
            end;
          end;
        finally
          FreeMem(FileBuffer);
        end;
      finally
        FreeAndNil(FileStream);
      end;
    finally
      timeEndPeriod(TimerCaps.wPeriodMin);
    end;
  except
  end;
end;


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

  Descript: Initialization
  Notes   :
 ------------------------------------------------------------------------------}
initialization
  TBCClassFactory.CreatePropertyPage(TfrmProperties, CLSID_MajorPushSourcePropertyPage);
end.

