{******************************************************************************}
{ FileName............: MajorPushSourceUnit001                                 }
{ Project.............: DirectShow                                             }
{ Author(s)...........: MM                                                     }
{ Version.............: 1.00                                                   }
{------------------------------------------------------------------------------}
{  DirectShow push source filter                                               }
{                                                                              }
{  Note: Can not use MadExcept: problem when stopping a running graph          }
{                                                                              }
{                                                                              }
{  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                                           }
{******************************************************************************}
unit MajorPushSourceUnit001;


interface
uses
  ActiveX,
  BaseClass,
  Classes,
  DirectShow9,
  Messages,
  SysUtils,
  Windows;


const
  CVersion = $0100;
  CBuild   = $20060115;

  IID_IPersistStream                     : TGUID = '{0000010C-0000-0000-C000-000000000046}';
  IID_ISpecifyPropertyPages              : TGUID = '{B196B28B-BAB4-101A-B69C-00AA00341D07}';

  // Interfaces for the outside world (yes, not randomly created ....)
  IID_MajorPushSource                            = '{6D616A6F-7269-7479-6E6C-535243020100}';
  T_IID_MajorPushSource                  : TGUID = IID_MajorPushSource;

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

  SudPinTypes : TRegPinTypes =
    (clsMajorType: @MEDIATYPE_NULL;
     clsMinorType: @MEDIASUBTYPE_None);

  SudPins : array[0..0] of TRegFilterPins =
    ((strName: 'Output'; bRendered: FALSE; bOutput: TRUE;  bZero: FALSE; bMany: FALSE; oFilter: nil; strConnectsToPin: ''; nMediaTypes: 1; lpMediaType: @SudPinTypes));


type
  // Simple object with fixed number of informational messages to hold/retrieve
  TInformation = class
    FInformation: array[0..127] of AnsiString;
    FInfoIn     : Integer;
    FInfoOut    : Integer;
    FInfoCount  : Integer;
    function  GetInformation: AnsiString;
    procedure SetInformation(Value: AnsiString);
  public
    constructor Create;
    destructor  Destroy; override;
    property Information: AnsiString read GetInformation write SetInformation;
    property Count      : Integer    read FInfoCount;
  end;

{------------------------------------------------------------------------------
  Descript: External access 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;


{------------------------------------------------------------------------------
  Descript: Output pin
 ------------------------------------------------------------------------------}
  TMajorPushSourceOutputPin = class(TBCSourceStream)
  protected
    FSharedState     : TBCCritSec;
    FInformation     : TInformation;
    FPushPinDataCount: Integer;
  public
    constructor Create(const ObjectName: AnsiString; out hr: HRESULT; Filter: TBCSource; const Name: WideString);
    destructor  Destroy; override;
    function  GetMediaType(MediaType: PAMMediaType): HRESULT; override;
    function  CheckMediaType(MediaType: PAMMediaType): HRESULT; override;
    function  DecideBufferSize(Alloc: IMemAllocator; propInputRequest: PAllocatorProperties): HRESULT; override;

    function  ThreadProc: Dword; override;
    function  FillBuffer(Sample: IMediaSample): HRESULT; override;
  end;


{------------------------------------------------------------------------------
  Descript: The filter
  Notes   : The <IPersistStream> is required to make the filter 'saveable'
            and 'loadable' (eg. to save/restore settings)
            The <IPersistStream> retains our settings.
 ------------------------------------------------------------------------------}
  TMajorPushSource = class(TBCSource, IMajorPushSource, ISpecifyPropertyPages, IPersistStream)
  private
    FThisInstance    : Integer;
    FPushPin         : TMajorPushSourceOutputPin;          // The push source output pin
    FInformation     : TInformation;                       // Information
    FLogStream       : TFileStream;                        // Filestream for information log
    FAddTimestamps   : Boolean;                            // True will add timestamps to media samples
    FLastTimestamp   : TReferenceTime;                     // Last used timestamp
    FMediaTypeMajor  : TCLSID;                             // Major  media type to use
    FMediaTypeMinor  : TCLSID;                             // Minor  media type to use
    FMediaTypeFormat : TCLSID;                             // Format media type to use
    FMediaTypeCheck  : Boolean;                            // True will check for correct media type
    FBufferSize      : Integer;                            // Total buffer size
    FBufferAlignment : Integer;                            // Alignment of buffer
    FBufferSizeD     : Integer;                            // Total -decided- buffer size
    FBufferAlignmentD: Integer;                            // Decided alignment of buffer (not necessarily active!)
    // Media sample buffer
    FPushSample      : IMediaSample;                       // The media sample for pushing data to
    FPushIn          : Integer;                            // Bytes already in input buffer
    FPushInSize      : Integer;                            // Bytes available in input buffer
    FPushInPtr       : PByte;                              // Pointer to input buffer
    // Pushed data buffer
    FPushedDataLock  : TBCCritSec;                         // Lock for pushed data buffer
    FPushedInPtr     : PByte;                              // Pointer to begin input buffer
    FPushedIn        : Integer;                            // Bytes already in input buffer
    FPushedInSize    : Integer;                            // Bytes available in input buffer
    FPushedInPtrPtr  : PByte;                              // Current pointer into input buffer
  protected
  public
    constructor Create(ObjName: AnsiString; unk: IUnknown; out hr: HRESULT);
    constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
    destructor  Destroy; override;

    // Required methods to implement (Source)

    // <ISpecifyPropertyPages>
    function    GetPages(out Pages: TCAGUID): HRESULT;    stdcall;

    // <IPersistStream>
    function    IsDirty: HRESULT; stdcall;
    function    Load(const stm: IStream): HRESULT; stdcall;
    function    Save(const stm: IStream; fClearDirty: BOOL): HRESULT; stdcall;
    function    GetSizeMax(out cbSize: Largeint): HRESULT; stdcall;

    // Specials
    function    NonDelegatingQueryInterface(const IID: TGUID; out Obj): HRESULT; override;

    // External accessible functions
    function  GetVersionInformation(out Info: PChar): HRESULT; stdcall;
    function  GetInformation       (out Info: PChar): HRESULT; stdcall;
    function  GetInformationCount  (out Count: Integer): HRESULT; stdcall;
    function  GetDeliveredCount    (out Count: Integer): HRESULT; stdcall;     // Get number of bytes delivered to output pin
    procedure SetLog               (OnOff: Boolean); stdcall;
    function  GetLog               (out OnOff: Boolean): HRESULT; stdcall;
    procedure SetAddTimestamps     (OnOff: Boolean); stdcall;
    function  GetAddTimestamps     (out OnOff: Boolean): HRESULT; stdcall;
    procedure SetMediaType         (MajorType: PCLSID; MinorType: PCLSID; FormatType: PCLSID; CheckType: Boolean); stdcall;
    function  GetMediaType         (MajorType: PCLSID; MinorType: PCLSID; FormatType: PCLSID; out CheckType: Boolean): HRESULT; stdcall;
    procedure SetBufferSize        (BufferSize: Integer; BufferAlignment: Integer); stdcall;
    function  GetBufferSize        (out BufferSize: Integer; out BufferAlignment: Integer): HRESULT; stdcall;
    function  GetPushedData        (Buffer: PByte; Size: Integer): HRESULT; stdcall;
    procedure FlushData; stdcall;
    function  PushData             (Buffer: PByte; Size: Integer; out Delivered: Integer): HRESULT; stdcall;

    // Internal functions
    procedure ToLog(Logstring: AnsiString);
  published
  end;

var
  InstanceCount: Integer;


implementation


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

  Descript: Create the information object.
  Notes   :
 ------------------------------------------------------------------------------}
constructor TInformation.Create;
begin
  inherited Create;

  FInfoIn    := 0;
  FInfoOut   := FInfoIn;
  FInfoCount := 0;
end;


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

  Descript: Destroy the information object.
  Notes   :
 ------------------------------------------------------------------------------}
destructor TInformation.Destroy;
begin
  inherited Destroy;
end;


{------------------------------------------------------------------------------
  Params  :
  Returns : <Result>  Empty if no information

  Descript: Get information.
  Notes   :
 ------------------------------------------------------------------------------}
function TInformation.GetInformation: AnsiString;
begin
  if FInfoOut = FInfoIn then
  begin
    Result := '';
    Exit;
  end;
  Result := FInformation[FInfoOut];
  Inc(FInfoOut);
  if FInfoOut > High(FInformation) then
    FInfoOut := Low(FInformation);
end;


{------------------------------------------------------------------------------
  Params  : <Value>   Information to add
  Returns : -

  Descript: Set information.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TInformation.SetInformation(Value: AnsiString);
begin
  FInformation[FInfoIn] := Value;
  Inc(FInfoCount);
  if FInfoCount = MaxInt then
    FInfoCount := 0;
  Inc(FInfoIn);
  if FInfoIn > High(FInformation) then
    FInfoIn := Low(FInformation);
end;


{------------------------------------------------------------------------------
  Params  : <ObjectName>  Name of the object
            <Filter>      Filter
            <Name>        Name of the pin to create
  Returns : <Hr>          Result of function

  Descript: Create the output pin.
  Notes   :
 ------------------------------------------------------------------------------}
constructor TMajorPushSourceOutputPin.Create(const ObjectName: AnsiString; out hr: HRESULT; Filter: TBCSource; const Name: WideString);
begin
  inherited Create(ObjectName, hr, Filter, Name);
  FSharedState      := TBCCritSec.Create;
  FInformation      := TInformation.Create;
  FPushPinDataCount := 0;
end;


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

  Descript: Destroy the output pin.
  Notes   :
 ------------------------------------------------------------------------------}
destructor TMajorPushSourceOutputPin.Destroy;
begin
  if Assigned(FInformation) then
    FreeAndNil(FInformation);
  if Assigned(FSharedState) then
    FreeAndNil(FSharedState);
  inherited Destroy;
end;


{------------------------------------------------------------------------------
  Params  : <MediaType>  Object receiving the media type.
  Returns : <Result>     Result (S_OK)

  Descript: Get preferred media type of output pin.
  Notes   : DirectShow
 ------------------------------------------------------------------------------}
function TMajorPushSourceOutputPin.GetMediaType(MediaType: PAMMediaType): HRESULT;
begin
  FFilter.StateLock.Lock;              // Note: FFilter is the parent
  try
//    FInformation.Information := 'TMajorPushSourceOutputPin.GetMediaType';
    MediaType.MajorType            := TMajorPushSource(FFilter).FMediaTypeMajor;
    MediaType.SubType              := TMajorPushSource(FFilter).FMediaTypeMinor;
    MediaType.bFixedSizeSamples    := True;
    MediaType.lSampleSize          := 0;
    MediaType.bTemporalCompression := False;
    MediaType.FormatType           := TMajorPushSource(FFilter).FMediaTypeFormat;
    MediaType.cbFormat             := 0;
    MediaType.pbFormat             := nil;
    FInformation.Information := format('Pin:GetMediaType major type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
      [MediaType.MajorType.D1,    MediaType.MajorType.D2,    MediaType.MajorType.D3,
       MediaType.MajorType.D4[0], MediaType.MajorType.D4[1],
       MediaType.MajorType.D4[2], MediaType.MajorType.D4[3], MediaType.MajorType.D4[4],
       MediaType.MajorType.D4[5], MediaType.MajorType.D4[6], MediaType.MajorType.D4[7]]);
    FInformation.Information := format('Pin:GetMediaType minor type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
      [MediaType.SubType.D1,    MediaType.SubType.D2,    MediaType.SubType.D3,
       MediaType.SubType.D4[0], MediaType.SubType.D4[1],
       MediaType.SubType.D4[2], MediaType.SubType.D4[3], MediaType.SubType.D4[4],
       MediaType.SubType.D4[5], MediaType.SubType.D4[6], MediaType.SubType.D4[7]]);
    FInformation.Information := format('Pin:GetMediaType format type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
      [MediaType.FormatType.D1,    MediaType.FormatType.D2,    MediaType.FormatType.D3,
       MediaType.FormatType.D4[0], MediaType.FormatType.D4[1],
       MediaType.FormatType.D4[2], MediaType.FormatType.D4[3], MediaType.FormatType.D4[4],
       MediaType.FormatType.D4[5], MediaType.FormatType.D4[6], MediaType.FormatType.D4[7]]);
    Result := S_OK;
  finally
    FFilter.StateLock.Unlock;
  end;
end;


{------------------------------------------------------------------------------
  Params  : <MediaType>  Pointer to media type object with the proposed media
                         type
  Returns : <Result>     S_OK    if correct media type (if checked)
                         S_FALSE if incorrect media type

  Descript: Check media type of output pin.
  Notes   : DirectShow
            The GUID is only checked if both GUIDs are not 'null'.
 ------------------------------------------------------------------------------}
function TMajorPushSourceOutputPin.CheckMediaType(MediaType: PAMMediaType): HRESULT;
begin
//  FInformation.Information := 'TMajorPushSourceOutputPin.CheckMediaType';
    FInformation.Information := format('Pin:CheckMediaType major type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
      [MediaType.MajorType.D1,    MediaType.MajorType.D2,    MediaType.MajorType.D3,
       MediaType.MajorType.D4[0], MediaType.MajorType.D4[1],
       MediaType.MajorType.D4[2], MediaType.MajorType.D4[3], MediaType.MajorType.D4[4],
       MediaType.MajorType.D4[5], MediaType.MajorType.D4[6], MediaType.MajorType.D4[7]]);
    FInformation.Information := format('Pin:CheckMediaType minor type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
      [MediaType.SubType.D1,    MediaType.SubType.D2,    MediaType.SubType.D3,
       MediaType.SubType.D4[0], MediaType.SubType.D4[1],
       MediaType.SubType.D4[2], MediaType.SubType.D4[3], MediaType.SubType.D4[4],
       MediaType.SubType.D4[5], MediaType.SubType.D4[6], MediaType.SubType.D4[7]]);
    FInformation.Information := format('Pin:CheckMediaType format type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
      [MediaType.FormatType.D1,    MediaType.FormatType.D2,    MediaType.FormatType.D3,
       MediaType.FormatType.D4[0], MediaType.FormatType.D4[1],
       MediaType.FormatType.D4[2], MediaType.FormatType.D4[3], MediaType.FormatType.D4[4],
       MediaType.FormatType.D4[5], MediaType.FormatType.D4[6], MediaType.FormatType.D4[7]]);
  if  TMajorPushSource(FFilter).FMediaTypeCheck then
  begin
    FFilter.StateLock.Lock;              // Note: FFilter is the parent
    try
      Result := S_FALSE;
      if (not IsEqualGUID(MediaType.MajorType, GUID_NULL)) and
         (not IsEqualGUID(TMajorPushSource(FFilter).FMediaTypeMajor, GUID_NULL)) then
        if not IsEqualGUID(MediaType.MajorType, TMajorPushSource(FFilter).FMediaTypeMajor) then
          Exit;
      if (not IsEqualGUID(MediaType.SubType, GUID_NULL)) and
         (not IsEqualGUID(TMajorPushSource(FFilter).FMediaTypeMinor, GUID_NULL)) then
        if not IsEqualGUID(MediaType.SubType, TMajorPushSource(FFilter).FMediaTypeMinor) then
          Exit;
      if (not IsEqualGUID(MediaType.FormatType, GUID_NULL)) and
         (not IsEqualGUID(TMajorPushSource(FFilter).FMediaTypeFormat, GUID_NULL)) then
        if not IsEqualGUID(MediaType.FormatType, TMajorPushSource(FFilter).FMediaTypeFormat) then
          Exit;
    finally
      FFilter.StateLock.Unlock;
    end;
  end;
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : <Alloc>            Pointer to allocaters interface
            <propInpuRequest>  Input pin buffer requirements
  Returns :

  Descript: Place data in output pin buffer
  Notes   : DirectShow
 ------------------------------------------------------------------------------}
function TMajorPushSourceOutputPin.DecideBufferSize(Alloc: IMemAllocator; propInputRequest: PAllocatorProperties): HRESULT;
var
  Actual: TAllocatorProperties;
begin
  FFilter.StateLock.Lock;              // Note: FFilter is the parent
  try
//    FInformation.Information := 'TMajorPushSourceOutputPin.DecideBufferSize';
    Result := E_FAIL;
    if not Assigned(Alloc) then
      Exit;
    if not Assigned(propInputRequest) then
      Exit;
    // Ensure a minimum number of buffers
    if propInputRequest.cBuffers = 0 then
      propInputRequest.cBuffers := 1;
    propInputRequest.cbBuffer := TMajorPushSource(FFilter).FBufferSize;
    propInputRequest.cbAlign  := TMajorPushSource(FFilter).FBufferAlignment;
    propInputRequest.cbPrefix := 0;
    // Specify our buffer requirements
    Result := Alloc.SetProperties(propInputRequest^, Actual);
    if Failed(Result) then
      Exit;
    if (Actual.cBuffers < propInputRequest.cBuffers) then
      Result := E_FAIL
    else
      Result := S_OK;
    // Record decided parameters
    TMajorPushSource(FFilter).FBufferAlignmentD := Actual.cbAlign;
    TMajorPushSource(FFilter).FBufferSizeD      := Actual.cbBuffer;
    FInformation.Information := format('DecideBufferSize: %d bytes buffer, %d bytes alignment', [Actual.cbBuffer, Actual.cbAlign]);
  finally
    FFilter.StateLock.Unlock;
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Sample>  Pointer to an empty media sample interface
  Returns : <Result>  S_FALSE if end of stream
                      S_OK if success

  Descript: Our own ThreadProc (with very small exceptions equal to original
            ThreadProc code from BasClasses).
  Notes   : This one does not call DoBufferProcessingLoop, so we can deliver
            samples elsewhere.
 ------------------------------------------------------------------------------}
function TMajorPushSourceOutputPin.ThreadProc: Dword;
var
  com, cmd: TThreadCommand;
begin
  repeat
	  com := GetRequest;
  	if (com <> CMD_INIT) then
    begin
	    FThread.Reply(DWORD(E_UNEXPECTED));
	  end;
  until (com = CMD_INIT);

  Result := OnThreadCreate; // perform set up tasks
  if FAILED(Result) then
  begin
    OnThreadDestroy();
    FThread.Reply(Result);	// send failed return code from OnThreadCreate
    Result := 1;
    Exit;
  end;

  // Initialisation succeeded
  FThread.Reply(NOERROR);
  repeat
    cmd := GetRequest;
    // "repeat..until false" ensures, that if cmd = CMD_RUN
    // the next executing block will be CMD_PAUSE handler block.
    // This corresponds to the original C "switch" functionality
    repeat
      sleep(50);     // Added (no visible effects though ....?? )
      case cmd of
        CMD_EXIT, CMD_STOP:
          begin
            FThread.Reply(NOERROR);
            Break;
          end;
        CMD_RUN:
          begin
            cmd := CMD_PAUSE;
          end;
        CMD_PAUSE:
          begin
            FThread.Reply(NOERROR);
// Delvering samples done elsewhere....    DoBufferProcessingLoop;
            Break;
          end;
      else
        FThread.Reply(DWORD(E_NOTIMPL));
        Break;
      end;
    until False;
  until (cmd = CMD_EXIT);
  Result := OnThreadDestroy;	// tidy up.
  if FAILED(Result) then
  begin
    Result := 1;
    Exit;
  end;
  Result := 0;
end;


{------------------------------------------------------------------------------
  Params  : <Sample>  Pointer to an empty media sample interface
  Returns : <Result>  S_FALSE if end of stream
                      S_OK if success

  Descript: Fill a media sample with data.
  Notes   : DirectShow
            The source stream class has the DoBufferProcessingLoop method
            which calls this function in a loop (this loop ends if a
            media sample is rejected; the FillBuffer returns S_FALSE or a
            stop request is received).
            The filter graph must set to 'run' to have this function called.
            Note that because we have called 'Inactive' when we created the
            pin this will never be called.
            If the DoBufferProcessingLoop was running then we have to either
            return S_OK or S_FALSE. Unfortunately, with S_OK we also deliver
            a sample, and with S_FALSE we stop the graph running ....
            Because ThreadProc is overridden in our code, this code is no
            longer executed. However, it still exists so when the original
            ThreadProc is used the code still functions.
 ------------------------------------------------------------------------------}
function TMajorPushSourceOutputPin.FillBuffer(Sample: IMediaSample): HRESULT;
begin
//  FInformation.Information := 'TMajorPushSourceOutputPin.FillBuffer';
//  Result := S_OK;
  // Since we are not using the FillBuffer, but handle the uploading ourselves,
  // we give a negative result which stops calling FillBuffer.
  // However, this alos stops the graph .... when called from withn GraphEdit.
  Result := S_FALSE;
end;


{------------------------------------------------------------------------------
  Params  : <ObjName>  Object name
            <Unk>      Interface
  Returns : <hr>       Result

  Descript: Create filter.
  Notes   :
 ------------------------------------------------------------------------------}
constructor TMajorPushSource.Create(ObjName: AnsiString; unk: IUnknown; out hr: HRESULT);
var
  MajorType : TCLSID;
  MinorType : TCLSID;
  FormatType: TCLSID;
begin
  inherited Create(ObjName, unk, CLSID_MajorPushSource, hr);

  FThisInstance := InterlockedIncrement(InstanceCount);

  FInformation := TInformation.Create;
  FLogStream   := nil;

  FPushSample := nil;
  FPushInPtr  := nil;
  FPushIn     := 0;
  FPushInSize := 0;

  FPushedDataLock := TBCCritSec.Create;
  FPushedInPtrPtr := nil;
  FPushedIn       := 0;
  FPushedInSize   := 0;
  FPushedInPtr    := nil;

  FAddTimestamps := False;

  // Set default settings
  MajorType  := MEDIATYPE_Stream;
  MinorType  := MEDIASUBTYPE_MPEG2_TRANSPORT;
  FormatType := GUID_NULL;
  SetMediaType(@MajorType, @MinorType, @FormatType, False);
  SetBufferSize(188 * 512, 188);
  SetAddTimestamps(False);
  SetLog(False);

  // Create our output pin
  FPushPin := TMajorPushSourceOutputPin.Create(ObjName, hr, Self, 'Output');
  if not Assigned(FPushPin) then
    hr := S_FALSE
//  ToLog('TMajorPushSource.Create');
end;


{------------------------------------------------------------------------------
  Params  : <Factory>
            <Controller>
  Returns : <hr>

  Descript: Create from factory.
  Notes   : DirectShow
 ------------------------------------------------------------------------------}
constructor TMajorPushSource.CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown);
var
  hr: HRESULT;
begin
  Create(Factory.Name, Controller, hr);
end;


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

  Descript: Destructor.
  Notes   :
 ------------------------------------------------------------------------------}
destructor TMajorPushSource.Destroy;
begin
  // Release all created instances/objects
  if Assigned(FInformation) then
    FreeAndNil(FInformation);
  if Assigned(FPushSample) then
    FPushSample := nil;
  if Assigned(FPushPin) then
    FreeAndNil(FPushPin);
  if Assigned(FPushedInPtr) then
    FreeMem(FPushedInPtr);
  FPushedInPtr    := nil;
  FPushedInPtrPtr := nil;
  if Assigned(FPushedDataLock) then
    FreeAndNil(FPushedDataLock);
  SetLog(False);
  inherited;
end;


{------------------------------------------------------------------------------
  Params  : <LogString>  Text to output to log file
  Returns : -

  Descript: Put text to log file
  Notes   :
 ------------------------------------------------------------------------------}
procedure TMajorPushSource.ToLog(LogString: AnsiString);
var
  NewLog: AnsiString;
begin
  FInformation.Information := LogString;
  if not Assigned(FLogStream) then
    Exit;
  NewLog := FormatDateTime('YYYYMMDD"T"HHMMSS"  "', Now) + LogString + #13#10;
  FLogStream.Write(NewLog[1], Length(NewLog));
end;


{------------------------------------------------------------------------------
  Params  : <OnOff>    TRUE to activate log (new file will be created)
                       FALSE to stop log
  Returns : -

  Descript: (De)activate logging mechanism.
  Notes   : Activating an already started logging has no effect.
 ------------------------------------------------------------------------------}
procedure TMajorPushSource.SetLog(OnOff: Boolean);
begin
  if not OnOff and Assigned(FLogStream) then
  begin
    ToLog('Log stopped');
    FreeAndNil(FLogStream);
  end;
  if OnOff and not Assigned(FLogStream) then
  begin
    FLogStream := TFileStream.Create(FormatDateTime('YYYYMMDD"T"HHMMSS', Now) + '.LOG', fmCreate);
    ToLog('Log started');
  end;
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>   S_OK
            <OnOff>    TRUE if activate log
                       FALSE if no active log

  Descript: Get activation logging mechanism.
  Notes   :
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetLog(out OnOff: Boolean): HRESULT;
begin
//  ToLog('TMajorPushSource.GetLog');
  OnOff  := Assigned(FLogStream);
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : <OnOff>    TRUE to activate timestamps
                       FALSE to deactivate timestamps
  Returns : -

  Descript: (De)activate timestamps.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TMajorPushSource.SetAddTimestamps(OnOff: Boolean);
begin
  FAddTimestamps := OnOff;
  if OnOff then
    ToLog('Timestamps set on')
  else
    ToLog('Timestamps set off');
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>   S_OK
            <OnOff>    TRUE if activate timestamps
                       FALSE if no active timestamps

  Descript: Get activation timestamps mechanism.
  Notes   :
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetAddTimestamps(out OnOff: Boolean): HRESULT;
begin
//  ToLog('TMajorPushSource.GetAddTimestamps');
  OnOff  := FAddTimestamps;
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : <MajorType>   Pointer to structure with major  media type to use
            <MinorType>   Pointer to structure with minor  media type to use
            <FormatType>  Pointer to structure with format media type to use
            <CheckType>   True will check media types (major/minor only)
  Returns : -

  Descript: Set media type to use.
  Notes   : Must be called before the output pin is connected.
 ------------------------------------------------------------------------------}
procedure TMajorPushSource.SetMediaType(MajorType: PCLSID; MinorType: PCLSID; FormatType: PCLSID; CheckType: Boolean); stdcall;
begin
//  ToLog('TMajorPushSource.SetMediaType');
  if Assigned(MajorType) then
    FMediaTypeMajor  := MajorType^;
  if Assigned(MinorType) then
    FMediaTypeMinor  := MinorType^;
  if Assigned(FormatType) then
    FMediaTypeFormat := FormatType^;
  FMediaTypeCheck := CheckType;  
  // Could use StringFromGUID2, but why not do it ourselves
  ToLog(format('SetMediaType major type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
    [FMediaTypeMajor.D1,    FMediaTypeMajor.D2,    FMediaTypeMajor.D3,
     FMediaTypeMajor.D4[0], FMediaTypeMajor.D4[1],
     FMediaTypeMajor.D4[2], FMediaTypeMajor.D4[3], FMediaTypeMajor.D4[4],
     FMediaTypeMajor.D4[5], FMediaTypeMajor.D4[6], FMediaTypeMajor.D4[7]]));
  ToLog(format('SetMediaType minor type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
    [FMediaTypeMinor.D1,    FMediaTypeMinor.D2,    FMediaTypeMinor.D3,
     FMediaTypeMinor.D4[0], FMediaTypeMinor.D4[1],
     FMediaTypeMinor.D4[2], FMediaTypeMinor.D4[3], FMediaTypeMinor.D4[4],
     FMediaTypeMinor.D4[5], FMediaTypeMinor.D4[6], FMediaTypeMinor.D4[7]]));
  ToLog(format('SetMediaType format type: {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}',
    [FMediaTypeFormat.D1,    FMediaTypeFormat.D2,    FMediaTypeFormat.D3,
     FMediaTypeFormat.D4[0], FMediaTypeFormat.D4[1],
     FMediaTypeFormat.D4[2], FMediaTypeFormat.D4[3], FMediaTypeFormat.D4[4],
     FMediaTypeFormat.D4[5], FMediaTypeFormat.D4[6], FMediaTypeFormat.D4[7]]));
  if FMediaTypeCheck then
    ToLog('SetMediaType type will be checked')
  else
    ToLog('SetMediaType type will not be checked');
end;


{------------------------------------------------------------------------------
  Params  : <MajorType>   Pointer to structure with major  media type to return
            <MinorType>   Pointer to structure with minor  media type to return
            <FormatType>  Pointer to structure with format media type to return
  Returns : <Result>      Result code (S_OK)
            <CheckType>   Type is checked or not

  Descript: Get media type to use.
  Notes   :
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetMediaType(MajorType: PCLSID; MinorType: PCLSID; FormatType: PCLSID; out CheckType: Boolean): HRESULT; stdcall;
begin
//  ToLog('TMajorPushSource.GetMediaType');
  if Assigned(MajorType) then
    MajorType^  := FMediaTypeMajor;
  if Assigned(MinorType) then
    MinorType^  := FMediaTypeMinor;
  if Assigned(FormatType) then
    FormatType^ := FMediaTypeFormat;
  CheckType := FMediaTypeCheck;
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : <BufferSize>       Proposed buffer size
            <BufferAlignment>  Alignment of buffer (0 if none)
  Returns : -

  Descript: Set preferred buffer sizes to use.
  Notes   : Must be called before the output pin is connected.
            Presets the -decided- buffer size/alignment equal to the preferred
            buffer size/alignment.
 ------------------------------------------------------------------------------}
procedure TMajorPushSource.SetBufferSize(BufferSize: Integer; BufferAlignment: Integer); stdcall;
begin
//  ToLog('TMajorPushSource.SetBufferSize');
  FBufferSize       := BufferSize;
  FBufferAlignment  := BufferAlignment;
  FBufferSizeD      := FBufferSize;
  FBufferAlignmentD := FBufferAlignment;
  ToLog(format('SetBufferSize, size %d, alignment %d', [FBufferSize, FBufferAlignment]));
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>           S_OK
            <BufferSize>       Decided buffer size
            <BufferAlignment>  Alignment of buffer (0 if none)

  Descript: Get -decided- buffer sizes to use.
  Notes   : This is -not- the preferred buffer size/alignment, but the actual
            buffer size/alignment (assuming the pin has been connected!)
            When the pin has not been connected the returned values are not
            valid (they return the preferred values).
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetBufferSize(out BufferSize: Integer; out BufferAlignment: Integer): HRESULT; stdcall;
begin
//  ToLog('TMajorPushSource.SetBufferSize');
  BufferSize      := FBufferSizeD;
  BufferAlignment := FBufferAlignmentD;
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>  Result
            <Pages>   Property pages GUID

  Descript: Get property pages.
  Notes   : DirectShow
            <ISpecifyPropertyPages>
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetPages(out pages: TCAGUID): HRESULT;
begin
//  ToLog('TMajorPushSource.GetPages');
  Pages.cElems := 2;
  Pages.pElems := CoTaskMemAlloc(SizeOf(TGUID) * Pages.cElems);
  if not Assigned(Pages.pElems) then
  begin
    ToLog('TMajorPushSource.GetPages failed');
    Result := E_OUTOFMEMORY;
    Exit;
  end;
  Pages.pElems^[0] := CLSID_MajorPushSourcePropertyPage;
  Pages.pElems^[1] := CLSID_MajorPushSourcePropertyPageAbout;
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>  S_FALSE if does not need saving
                      S_OK    if needs saving

  Descript: Indicate if a property has changed.
  Notes   : DirectShow
            <IPersistStream>
 ------------------------------------------------------------------------------}
function TMajorPushSource.IsDirty: HRESULT;
begin
//  ToLog('TMajorPushSource.IsDirty');
  Result := S_FALSE;
end;


{------------------------------------------------------------------------------
  Params  : <stm>     Stream data
  Returns : <Result>

  Descript: Load setting data from stream.
  Notes   : DirectShow
            <IPersistStream>
 ------------------------------------------------------------------------------}
function TMajorPushSource.Load(const stm: IStream): HRESULT;
begin
//  ToLog('TMajorPushSource.Load');
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : <stm>          Stream data
            <fClearDirty>  True reset dirty flag
  Returns : <Result>

  Descript: Save setting data to stream.
  Notes   : DirectShow
            <IPersistStream>
 ------------------------------------------------------------------------------}
function TMajorPushSource.Save(const stm: IStream; fClearDirty: BOOL): HRESULT;
begin
//  ToLog('TMajorPushSource.Save');
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  :
  Returns : <Result>
            <cbSize>  Stream size

  Descript: Needed bytes for stream.
  Notes   : DirectShow
            <IPersistStream>
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetSizeMax(out cbSize: Largeint): HRESULT;
begin
//  ToLog('TMajorPushSource.GetSizeMax');
  Result := S_OK;
  cbSize := 0;
end;


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

  Descript: Support for delegating and non delegating IUnknown interfaces.
  Notes   : DirectShow
 ------------------------------------------------------------------------------}
function TMajorPushSource.NonDelegatingQueryInterface(const IID: TGUID; out Obj): HRESULT;
begin
//  ToLog('TMajorPushSource.NonDelegatingQueryInterface');
  Result := E_NOINTERFACE;
  // Since we are dealing with 16 byte long types we can not just use '=' on it
  // without typecasting or using <IsEqualGUID> (or simular)
  if IsEqualGUID(IID, T_IID_MajorPushSource) then
  begin
    if GetInterface(IMajorPushSource, Obj) then
    begin
//      ToLog('TMajorPushSource.NonDelegatingQueryInterface IMajorPushSource success');
      Result := S_OK;
    end
    else
      ToLog('TMajorPushSource.NonDelegatingQueryInterface IMajorPushSource failed');
    Exit;
  end;
  if IsEqualGUID(IID, IID_ISpecifyPropertyPages) then
  begin
    if GetInterface(ISpecifyPropertyPages, Obj) then
    begin
//      ToLog('TMajorPushSource.NonDelegatingQueryInterface ISpecifyPropertyPages success');
      Result := S_OK;
    end
    else
      ToLog('TMajorPushSource.NonDelegatingQueryInterface ISpecifyPropertyPages failed');
    Exit;
  end;
  if IsEqualGUID(IID, IID_IPersistStream) then
  begin
    if GetInterface(IPersistStream, Obj) then
    begin
//      ToLog('TMajorPushSource.NonDelegatingQueryInterface IPersistStream success');
      Result := S_OK;
    end
    else
      ToLog('TMajorPushSource.NonDelegatingQueryInterface IPersistStream failed');
    Exit;
  end;
  Result := inherited NonDelegatingQueryInterface(IID, Obj);
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>  S_OK
            <Info>    Informational data

  Descript: Get version information data.
  Notes   :
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetVersionInformation(out Info: PChar): HRESULT;
begin
//  ToLog('TMajorPushSource.GetVersionInformation');
  Result := S_FALSE;
  if not Assigned(Info) then
    Exit;
  StrPCopy(Info, format('V%1.1x.%2.2x, build %x', [CVersion div $100, CVersion mod $100, CBuild]));
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : <Info>    Pointer to PCHAR receiving the result (must have enough
                      memory allocated!)
  Returns : <Result>  S_OK
                      S_FALSE  Error (no result pointer)

  Descript: Get information data (also from output pin).
  Notes   : Only new data is returned!
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetInformation(out Info: PChar): HRESULT;
begin
//  ToLog('TMajorPushSource.GetInformation');
  Result := S_FALSE;
  if not Assigned(Info) then
    Exit;
  StrPCopy(Info, FInformation.Information);
  if Length(Info) = 0 then
    if Assigned(FPushPin) then
      StrPCopy(Info, FPushPin.FInformation.Information);
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>  S_OK
            <Count>   Information counter

  Descript: Get information count (also from output pin).
  Notes   : 
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetInformationCount(out Count: Integer): HRESULT;
begin
//  ToLog('TMajorPushSource.GetInformationCount');
  Count := FInformation.Count;
  if Assigned(FPushPin) then
    Count := Count + FPushPin.FInformation.Count;
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>  S_OK
                      S_FALSE  No output pin
            <Count>   Transferred bytes

  Descript: Get information count of delivered data on output pin.
  Notes   :
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetDeliveredCount(out Count: Integer): HRESULT;
begin
//  ToLog('TMajorPushSource.GetDeliveredCount');
  Result := S_FALSE;
  if not Assigned(FPushPin) then
    Exit;
  Count := FPushPin.FPushPinDataCount;
  Result := S_OK;
end;


{------------------------------------------------------------------------------
  Params  : <Buffer>  Pointer to buffer receiving the data
            <Size>    Size of the buffer (and new size of acquiring buffer)
                      0 to disable acquiring data
  Returns : <Result>  S_OK

  Descript: Get acquires pushed data (first data of media sample)
  Notes   : If the <Size> is different from the current acquireing size then
            a new buffer is allocated.
 ------------------------------------------------------------------------------}
function TMajorPushSource.GetPushedData(Buffer: PByte; Size: Integer): HRESULT;
begin
//  ToLog('TMajorPushSource.GetPushedData');
  Result := S_OK;
  FPushedDataLock.Lock;
  try
    // New size
    if (Size <> FPushedInSize) then
    begin
      ToLog(format('GetPushedData new size of %d bytes', [Size]));
      if (Size > 0) and Assigned(Buffer) and Assigned(FPushedInPtr) then
      begin
        // Something to copy ....
        // Copy current contents of buffer
        if Size < FPushedInSize then
        begin
          CopyMemory(Buffer, FPushedInPtr, Size);
        end
        else
        begin
          // We have less data than requested: return zero for those
          CopyMemory(Buffer, FPushedInPtr, FPushedInSize);
          Inc(Buffer, FPushedInSize);
          ZeroMemory(Buffer, Size - FPushedInSize);
        end;
      end;
      // Release old buffer
      FreeMem(FPushedInPtr);
      // Allocate new buffer and reset settings
      GetMem(FPushedInPtr, Size);
      FPushedInSize   := Size;
      FPushedInPtrPtr := FPushedInPtr;
      FPushedIn       := 0;
    end
    else
    begin
      // Same size, return buffer contents if buffers exist
      if (Size > 0) and Assigned(Buffer) and Assigned(FPushedInPtr) then
        CopyMemory(Buffer, FPushedInPtr, FPushedInSize);
    end;
  finally
    FPushedDataLock.Unlock;
  end;
end;


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

  Descript: Flush (re-allign) for next data.
  Notes   : Some filters require correctly aligned data. The one pushing
            the data should keep this into mind, but to force a re-alignment
            this procedure can be called. Any old buffer data is discarded.
 ------------------------------------------------------------------------------}
procedure TMajorPushSource.FlushData;
begin
//  ToLog('TMajorPushSource.FlushData');
  StateLock.Lock;
  try
    if Assigned(FPushSample) then
      FPushSample := nil;
  finally
    StateLock.Unlock;
  end;
end;


{------------------------------------------------------------------------------
  Params  : <Buffer>     Pointer to buffer data
            <Size>       Size of buffer
  Returns : <Result>     S_OK     if data can be delivered
                         S_FALSE  if an error was detected (e.g. when graph
                                  not running etc).
            <Delivered>  Bytes actually delivered through media sample
                         (note: not necessarily equal to <Size> bytes because
                         the media sample buffer might be smaller/larger or
                         'out of sync')

  Descript: Push source data.
  Notes   : No intermediate buffers. Data is directly copied to the media
            sample.
            A typical push source filter would use FillBuffer instead, which
            is called automatically for every media sample. Since our
            FillBuffer returns a S_FALSE, this is not the case.....
 ------------------------------------------------------------------------------}
function TMajorPushSource.PushData(Buffer: PByte; Size: Integer; out Delivered: Integer): HRESULT;
var
  CopyBytes   : Integer;
  ToCopy      : Integer;
  EndTime     : REFERENCE_TIME;
begin
  Result    := S_FALSE;
  Delivered := 0;
  // Lock the filter so it can not be removed
  StateLock.Lock;
  try
    ToCopy := Size;
    // Accumulate data into media sample until a full media buffer is accumulated
    // and then deliver it
    while (ToCopy <> 0) do
    begin
      // Get a new media sample for the input pin (we copy directly to it)
      // if there is not yet one
      if not Assigned(FPushSample) then
      begin
        // Get media sample
        if (FPushPin.GetDeliveryBuffer(FPushSample, nil, nil, 0) <> S_OK) then
        begin
          FPushSample := nil;
          Exit;
        end;
        // Get pointer to media sample
        if Failed(FPushSample.GetPointer(FPushInPtr)) then
        begin
          FPushSample := nil;
          Exit;
        end;
        // Get size of media sample buffer and reset data counter
        FPushInSize := FPushSample.GetSize;
        FPushIn     := 0;
        // Pushed data buffer reset (the pushed data buffer buffers the first FPushedInSize data)
        FPushedDataLock.Lock;
        try
          FPushedInPtrPtr := FPushedInPtr;
          FPushedIn       := 0;
        finally
          FPushedDataLock.Unlock;
        end;
      end;

      // Pushed data buffer
      FPushedDataLock.Lock;
      try
        if Assigned(FPushedInPtr) then
        begin
          // Only copy if not yet full
          if (FPushedIn < FPushedInSize) then
          begin
            CopyBytes := ToCopy;
            if (ToCopy + FPushedIn) >= FPushedInSize then
              CopyBytes := FPushedInSize - FPushedIn;
            CopyMemory(FPushedInPtrPtr, Buffer, CopyBytes);
            // Adjust pointers/counters
            Inc(FPushedInPtrPtr, CopyBytes);
            Inc(FPushedIn,       CopyBytes);
          end;
        end;
      finally
        FPushedDataLock.Unlock;
      end;

      // Copy as much of data to the media sample as possible
      CopyBytes := ToCopy;
      if (ToCopy + FPushIn) >= FPushInSize then
        CopyBytes := FPushInSize - FPushIn;
      CopyMemory(FPushInPtr, Buffer, CopyBytes);
      // Adjust pointers/counters
      Inc(FPushInPtr, CopyBytes);
      Inc(FPushIn,    CopyBytes);
      Inc(Buffer,     CopyBytes);
      Dec(ToCopy,     CopyBytes);
      // If we have a full buffer then 'upload' it
      if FPushIn = FPushInSize then
      try
        // Add timestamps if requested (-not- accurate at all, just the
        // time from buffer to buffer)
        if FAddTimestamps then
        begin
          Self.StreamTime(EndTime);
          FPushSample.SetTime(@FLastTimestamp, @EndTime);
          FLastTimestamp := EndTime;
        end;
        if FPushPin.Deliver(FPushSample) = S_OK then       // Deliver the media sample to the input pin
        begin
          Inc(Delivered, FPushInSize);                     // Adjust returned information
          Inc(FPushPin.FPushPinDataCount, FPushInSize);    // Adjust debug information
          if FPushPin.FPushPinDataCount > 1000000000 then  // Make sure it will not get too excessive
            FPushPin.FPushPinDataCount := 0;
        end;
      finally
        // After delivering the sample we make it available again
        FPushSample := nil;
      end;
    end;
    Result := S_OK;
  finally
    StateLock.Unlock;
  end;
end;


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

  Descript: Initialization
  Notes   : DirectShow
 ------------------------------------------------------------------------------}
initialization
  InstanceCount := 0;

  TBCClassFactory.CreateFilter(TMajorPushSource, 'Major Push Source', CLSID_MajorPushSource,
    CLSID_LegacyAmFilterCategory, MERIT_DO_NOT_USE, 1, @SudPins);
end.
