BaseClass.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:487k
- (*********************************************************************
- * DSPack 2.3.3 *
- * DirectShow BaseClass *
- * *
- * home page : http://www.progdigy.com *
- * email : hgourvest@progdigy.com *
- * *
- * date : 21-02-2003 *
- * *
- * The contents of this file are used with permission, subject to *
- * the Mozilla Public License Version 1.1 (the "License"); you may *
- * not use this file except in compliance with the License. You may *
- * obtain a copy of the License at *
- * http://www.mozilla.org/MPL/MPL-1.1.html *
- * *
- * Software distributed under the License is distributed on an *
- * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or *
- * implied. See the License for the specific language governing *
- * rights and limitations under the License. *
- * *
- * Contributor(s) *
- * Andriy Nevhasymyy <a.n@email.com> *
- * Milenko Mitrovic <dcoder@dsp-worx.de> *
- * Michael Andersen <michael@mechdata.dk> *
- * Martin Offenwanger <coder@dsplayer.de> *
- * *
- *********************************************************************)
- {.$DEFINE DEBUG} // Debug Log
- {.$DEFINE TRACE} // Trace Criteral Section (DEBUG must be ON)
- {.$DEFINE MESSAGE} // Use OutputDebugString instead of a File (DEBUG must be ON)
- {.$DEFINE PERF} // Show Performace Counter
- {.$DEFINE VTRANSPERF} // Show additional TBCVideoTransformFilter Performace Counter (PERF must be ON)
- {$MINENUMSIZE 4}
- {$ALIGN ON}
- unit BaseClass;
- {$IFDEF VER150}
- {$WARN UNSAFE_CODE OFF}
- {$WARN UNSAFE_TYPE OFF}
- {$WARN UNSAFE_CAST OFF}
- {$ENDIF}
- interface
- uses Windows, SysUtils, Classes, Math, ActiveX, Forms, Messages, Controls,
- DirectShow9, dialogs, ComObj, mmsystem, DSUtil;
- const
- OATRUE = -1;
- OAFALSE = 0;
- DEFAULTCACHE = 10; // Default node object cache size
- type
- TBCCritSec = class
- private
- FCritSec : TRTLCriticalSection;
- {$IFDEF DEBUG}
- FcurrentOwner: Longword;
- FlockCount : Longword;
- {$ENDIF}
- public
- constructor Create;
- destructor Destroy; override;
- procedure Lock;
- procedure UnLock;
- function CritCheckIn: boolean;
- function CritCheckOut: boolean;
- end;
- TBCBaseObject = class(TObJect)
- private
- FName: string;
- public
- constructor Create(Name: string);
- destructor Destroy; override;
- class function NewInstance: TObject; override;
- procedure FreeInstance; override;
- class function ObjectsActive: integer;
- end;
- TBCClassFactory = Class;
- TBCUnknown = class(TBCBaseObject, IUnKnown)
- private
- FRefCount: integer;
- FOwner : Pointer;
- protected
- function IUnknown.QueryInterface = NonDelegatingQueryInterface;
- function IUnknown._AddRef = NonDelegatingAddRef;
- function IUnknown._Release = NonDelegatingRelease;
- function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
- public
- function _AddRef: Integer; stdcall;
- function _Release: Integer; stdcall;
- constructor Create(name: string; Unk: IUnknown);
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); virtual;
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
- function NonDelegatingAddRef: Integer; virtual; stdcall;
- function NonDelegatingRelease: Integer; virtual; stdcall;
- function GetOwner: IUnKnown;
- end;
- TBCUnknownClass = Class of TBCUnknown;
- TFormPropertyPage = class;
- TFormPropertyPageClass = class of TFormPropertyPage;
- TBCBaseFilter = class;
- TBCBaseFilterClass = class of TBCBaseFilter;
- TBCClassFactory = class(TObject, IUnKnown, IClassFactory)
- private
- FNext : TBCClassFactory;
- FComClass : TBCUnknownClass;
- FPropClass: TFormPropertyPageClass;
- FName : String;
- FClassID : TGUID;
- FCategory : TGUID;
- FMerit : LongWord;
- FPinCount : Cardinal;
- FPins : PRegFilterPins;
- function RegisterFilter(FilterMapper: IFilterMapper; Register: Boolean): boolean; overload;
- function RegisterFilter(FilterMapper: IFilterMapper2; Register: Boolean): boolean; overload;
- procedure UpdateRegistry(Register: Boolean); overload;
- protected
- function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
- function _AddRef: Integer; stdcall;
- function _Release: Integer; stdcall;
- function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
- out Obj): HResult; stdcall;
- function LockServer(fLock: BOOL): HResult; stdcall;
- public
- constructor CreateFilter(ComClass: TBCUnknownClass; Name: string;
- const ClassID: TGUID; const Category: TGUID; Merit: LongWord;
- PinCount: Cardinal; Pins: PRegFilterPins);
- constructor CreatePropertyPage(ComClass: TFormPropertyPageClass; const ClassID: TGUID);
- property Name: String read FName;
- property ClassID: TGUID read FClassID;
- end;
- TBCFilterTemplate = class
- private
- FFactoryList : TBCClassFactory;
- procedure AddObjectFactory(Factory: TBCClassFactory);
- public
- constructor Create;
- destructor Destroy; override;
- function RegisterServer(Register: Boolean): boolean;
- function GetFactoryFromClassID(const CLSID: TGUID): TBCClassFactory;
- end;
- TBCMediaType = object
- MediaType: PAMMediaType;
- function Equal(mt: TBCMediaType): boolean; overload;
- function Equal(mt: PAMMediaType): boolean; overload;
- function MatchesPartial(Partial: PAMMediaType): boolean;
- function IsPartiallySpecified: boolean;
- function IsValid: boolean;
- procedure InitMediaType;
- function FormatLength: Cardinal;
- end;
- TBCBasePin = class;
- TBCBaseFilter = class(TBCUnknown, IBaseFilter, IAMovieSetup)
- protected
- FState : TFilterState; // current state: running, paused
- FClock : IReferenceClock; // this graph's ref clock
- FStart : TReferenceTime; // offset from stream time to reference time
- FCLSID : TGUID; // This filters clsid used for serialization
- FLock : TBCCritSec; // Object we use for locking
- FFilterName : WideString; // Full filter name
- FGraph : IFilterGraph; // Graph we belong to
- FSink : IMediaEventSink; // Called with notify events
- FPinVersion: Integer; // Current pin version
- public
- constructor Create(Name: string; // Object description
- Unk : IUnKnown; // IUnknown of delegating object
- Lock: TBCCritSec; // Object who maintains lock
- const clsid: TGUID // The clsid to be used to serialize this filter
- ); overload;
- constructor Create(Name: string; // Object description
- Unk : IUnKnown; // IUnknown of delegating object
- Lock: TBCCritSec; // Object who maintains lock
- const clsid: TGUID; // The clsid to be used to serialize this filter
- out hr: HRESULT // General OLE return code
- ); overload;
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
- destructor destroy; override;
- // --- IPersist method ---
- function GetClassID(out classID: TCLSID): HResult; stdcall;
- // --- IMediaFilter methods ---
- // override Stop and Pause so we can activate the pins.
- // Note that Run will call Pause first if activation needed.
- // Override these if you want to activate your filter rather than
- // your pins.
- function Stop: HRESULT; virtual; stdcall;
- function Pause: HRESULT; virtual; stdcall;
- // the start parameter is the difference to be added to the
- // sample's stream time to get the reference time for
- // its presentation
- function Run(tStart: TReferenceTime): HRESULT; virtual; stdcall;
- function GetState(dwMilliSecsTimeout: DWORD; out State: TFilterState): HRESULT; virtual; stdcall;
- function SetSyncSource(pClock: IReferenceClock): HRESULT; stdcall;
- function GetSyncSource(out pClock: IReferenceClock): HRESULT; stdcall;
- // --- helper methods ---
- // return the current stream time - ie find out what
- // stream time should be appearing now
- function StreamTime(out rtStream: TReferenceTime): HRESULT; virtual;
- // Is the filter currently active?
- function IsActive: boolean;
- // Is this filter stopped (without locking)
- function IsStopped: boolean;
- // --- IBaseFilter methods ---
- // pin enumerator
- function EnumPins(out ppEnum: IEnumPins): HRESULT; stdcall;
- // default behaviour of FindPin assumes pin ids are their names
- function FindPin(Id: PWideChar; out Pin: IPin): HRESULT; virtual; stdcall;
- function QueryFilterInfo(out pInfo: TFilterInfo): HRESULT; stdcall;
- // milenko start (added virtual to be able to override it in the renderers)
- function JoinFilterGraph(pGraph: IFilterGraph; pName: PWideChar): HRESULT; virtual; stdcall;
- // milenko end
- // return a Vendor information string. Optional - may return E_NOTIMPL.
- // memory returned should be freed using CoTaskMemFree
- // default implementation returns E_NOTIMPL
- function QueryVendorInfo(out pVendorInfo: PWideChar): HRESULT; stdcall;
- // --- helper methods ---
- // send an event notification to the filter graph if we know about it.
- // returns S_OK if delivered, S_FALSE if the filter graph does not sink
- // events, or an error otherwise.
- function NotifyEvent(EventCode, EventParam1, EventParam2: LongInt): HRESULT;
- // return the filter graph we belong to
- function GetFilterGraph: IFilterGraph;
- // Request reconnect
- // pPin is the pin to reconnect
- // pmt is the type to reconnect with - can be NULL
- // Calls ReconnectEx on the filter graph
- function ReconnectPin(Pin: IPin; pmt: PAMMediaType): HRESULT;
- // find out the current pin version (used by enumerators)
- function GetPinVersion: LongInt; virtual;
- procedure IncrementPinVersion;
- // you need to supply these to access the pins from the enumerator
- // and for default Stop and Pause/Run activation.
- function GetPinCount: integer; virtual; abstract;
- function GetPin(n: Integer): TBCBasePin; virtual; abstract;
- // --- IAMovieSetup methods ---
- {nev: start 04/16/04 added "virtual"}
- function Register: HRESULT; virtual; stdcall;
- function Unregister: HRESULT; virtual; stdcall;
- {nev: end}
- property State: TFilterState read FState;
- property GRaph : IFilterGraph read FGRaph;
- end;
- { NOTE The implementation of this class calls the CUnknown constructor with
- a NULL outer unknown pointer. This has the effect of making us a self
- contained class, ie any QueryInterface, AddRef or Release calls will be
- routed to the class's NonDelegatingUnknown methods. You will typically
- find that the classes that do this then override one or more of these
- virtual functions to provide more specialised behaviour. A good example
- of this is where a class wants to keep the QueryInterface internal but
- still wants its lifetime controlled by the external object }
- TBCBasePin = class(TBCUnknown, IPin, IQualityControl)
- protected
- FPinName: WideString;
- FConnected : IPin; // Pin we have connected to
- Fdir : TPinDirection; // Direction of this pin
- FLock : TBCCritSec; // Object we use for locking
- FRunTimeError : boolean; // Run time error generated
- FCanReconnectWhenActive: boolean; // OK to reconnect when active
- FTryMyTypesFirst : boolean; // When connecting enumerate
- // this pin's types first
- FFilter : TBCBaseFilter; // Filter we were created by
- FQSink : IQualityControl; // Target for Quality messages
- FTypeVersion : LongInt; // Holds current type version
- Fmt : TAMMediaType; // Media type of connection
- FStart : TReferenceTime; // time from NewSegment call
- FStop : TReferenceTime; // time from NewSegment
- FRate : double; // rate from NewSegment
- FRef : LongInt;
- function GetCurrentMediaType: TBCMediaType;
- function GetAMMediaType: PAMMediaType;
- protected
- procedure DisplayPinInfo(ReceivePin: IPin);
- procedure DisplayTypeInfo(Pin: IPin; pmt: PAMMediaType);
- // used to agree a media type for a pin connection
- // given a specific media type, attempt a connection (includes
- // checking that the type is acceptable to this pin)
- function AttemptConnection(
- ReceivePin: IPin; // connect to this pin
- pmt : PAMMediaType // using this type
- ): HRESULT;
- // try all the media types in this enumerator - for each that
- // we accept, try to connect using ReceiveConnection.
- function TryMediaTypes(
- ReceivePin: IPin; // connect to this pin
- pmt : PAMMediaType; // proposed type from Connect
- Enum : IEnumMediaTypes // try this enumerator
- ): HRESULT;
- // establish a connection with a suitable mediatype. Needs to
- // propose a media type if the pmt pointer is null or partially
- // specified - use TryMediaTypes on both our and then the other pin's
- // enumerator until we find one that works.
- function AgreeMediaType(
- ReceivePin: IPin; // connect to this pin
- pmt : PAMMediaType // proposed type from Connect
- ): HRESULT;
- function DisconnectInternal: HRESULT; stdcall;
- public
- function NonDelegatingAddRef: Integer; override; stdcall;
- function NonDelegatingRelease: Integer; override; stdcall;
- constructor Create(
- ObjectName: string; // Object description
- Filter : TBCBaseFilter; // Owning filter who knows about pins
- Lock : TBCCritSec; // Object who implements the lock
- out hr : HRESULT; // General OLE return code
- Name : WideString; // Pin name for us
- dir : TPinDirection); // Either PINDIR_INPUT or PINDIR_OUTPUT
- destructor destroy; override;
- // --- IPin methods ---
- // take lead role in establishing a connection. Media type pointer
- // may be null, or may point to partially-specified mediatype
- // (subtype or format type may be GUID_NULL).
- function Connect(pReceivePin: IPin; const pmt: PAMMediaType): HRESULT; virtual; stdcall;
- // (passive) accept a connection from another pin
- function ReceiveConnection(pConnector: IPin; const pmt: TAMMediaType): HRESULT; virtual; stdcall;
- function Disconnect: HRESULT; virtual; stdcall;
- function ConnectedTo(out pPin: IPin): HRESULT; virtual; stdcall;
- function ConnectionMediaType(out pmt: TAMMediaType): HRESULT; virtual; stdcall;
- function QueryPinInfo(out pInfo: TPinInfo): HRESULT; virtual; stdcall;
- function QueryDirection(out pPinDir: TPinDirection): HRESULT; stdcall;
- function QueryId(out Id: PWideChar): HRESULT; virtual; stdcall;
- // does the pin support this media type
- function QueryAccept(const pmt: TAMMediaType): HRESULT; virtual; stdcall;
- // return an enumerator for this pins preferred media types
- function EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT; virtual; stdcall;
- // return an array of IPin* - the pins that this pin internally connects to
- // All pins put in the array must be AddReffed (but no others)
- // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE
- // Default: return E_NOTIMPL
- // The filter graph will interpret NOT_IMPL as any input pin connects to
- // all visible output pins and vice versa.
- // apPin can be NULL if nPin==0 (not otherwise).
- function QueryInternalConnections(out apPin: IPin; var nPin: ULONG): HRESULT; virtual; stdcall;
- // Called when no more data will be sent
- function EndOfStream: HRESULT; virtual; stdcall;
- function BeginFlush: HRESULT; virtual; stdcall; abstract;
- function EndFlush: HRESULT; virtual; stdcall; abstract;
- // Begin/EndFlush still PURE
- // NewSegment notifies of the start/stop/rate applying to the data
- // about to be received. Default implementation records data and
- // returns S_OK.
- // Override this to pass downstream.
- function NewSegment(tStart, tStop: TReferenceTime; dRate: double): HRESULT; virtual; stdcall;
- // --- IQualityControl methods ---
- function Notify(pSelf: IBaseFilter; q: TQuality): HRESULT; virtual; stdcall;
- function SetSink(piqc: IQualityControl): HRESULT; virtual; stdcall;
- // --- helper methods ---
- // Returns True if the pin is connected. false otherwise.
- function IsConnected: boolean;
- // Return the pin this is connected to (if any)
- property GetConnected: IPin read FConnected;
- // Check if our filter is currently stopped
- function IsStopped: boolean;
- // find out the current type version (used by enumerators)
- function GetMediaTypeVersion: longint; virtual;
- procedure IncrementTypeVersion;
- // switch the pin to active (paused or running) mode
- // not an error to call this if already active
- function Active: HRESULT; virtual;
- // switch the pin to inactive state - may already be inactive
- function Inactive: HRESULT; virtual;
- // Notify of Run() from filter
- function Run(Start: TReferenceTime): HRESULT; virtual;
- // check if the pin can support this specific proposed type and format
- function CheckMediaType(mt: PAMMediaType): HRESULT; virtual; abstract;
- // set the connection to use this format (previously agreed)
- function SetMediaType(mt: PAMMediaType): HRESULT; virtual;
- // check that the connection is ok before verifying it
- // can be overridden eg to check what interfaces will be supported.
- function CheckConnect(Pin: IPin): HRESULT; virtual;
- // Set and release resources required for a connection
- function BreakConnect: HRESULT; virtual;
- function CompleteConnect(ReceivePin: IPin): HRESULT; virtual;
- // returns the preferred formats for a pin
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; virtual;
- // access to NewSegment values
- property CurrentStopTime: TReferenceTime read FStop;
- property CurrentStartTime: TReferenceTime read FStart;
- property CurrentRate: double read FRate;
- // Access name
- property Name: WideString read FPinName;
- property CanReconnectWhenActive: boolean read FCanReconnectWhenActive write FCanReconnectWhenActive;
- // Media type
- property CurrentMediaType: TBCMediaType read GetCurrentMediaType;
- property AMMediaType: PAMMediaType read GetAMMediaType;
- end;
- TBCEnumPins = class(TInterfacedObject, IEnumPins)
- private
- FPosition: integer; // Current ordinal position
- FPinCount: integer; // Number of pins available
- FFilter: TBCBaseFilter; // The filter who owns us
- FVersion: LongInt; // Pin version information
- // These pointers have not been AddRef'ed and
- // so they should not be dereferenced. They are
- // merely kept to ID which pins have been enumerated.
- FPinCache: TList;
- { If while we are retrieving a pin for example from the filter an error
- occurs we assume that our internal state is stale with respect to the
- filter (someone may have deleted all the pins). We can check before
- starting whether or not the operation is likely to fail by asking the
- filter what it's current version number is. If the filter has not
- overriden the GetPinVersion method then this will always match }
- function AreWeOutOfSync: boolean;
- (* This method performs the same operations as Reset, except is does not clear
- the cache of pins already enumerated. *)
- function Refresh: HRESULT; stdcall;
- public
- constructor Create(Filter: TBCBaseFilter; EnumPins: TBCEnumPins);
- destructor Destroy; override;
- function Next(cPins: ULONG; // place this many pins...
- out ppPins: IPin; // ...in this array of IPin*
- pcFetched: PULONG // actual count passed returned here
- ): HRESULT; stdcall;
- function Skip(cPins: ULONG): HRESULT; stdcall;
- function Reset: HRESULT; stdcall;
- function Clone(out ppEnum: IEnumPins): HRESULT; stdcall;
- end;
- TBCEnumMediaTypes = class(TInterfacedObject, IEnumMediaTypes)
- private
- FPosition: Cardinal; // Current ordinal position
- FPin : TBCBasePin; // The pin who owns us
- FVersion : LongInt; // Media type version value
- function AreWeOutOfSync: boolean;
- public
- constructor Create(Pin: TBCBasePin; EnumMediaTypes: TBCEnumMediaTypes);
- destructor Destroy; override;
- function Next(cMediaTypes: ULONG; out ppMediaTypes: PAMMediaType;
- pcFetched: PULONG): HRESULT; stdcall;
- function Skip(cMediaTypes: ULONG): HRESULT; stdcall;
- function Reset: HRESULT; stdcall;
- function Clone(out ppEnum: IEnumMediaTypes): HRESULT; stdcall;
- end;
- TBCBaseOutputPin = class(TBCBasePin)
- protected
- FAllocator: IMemAllocator;
- // interface on the downstreaminput pin, set up in CheckConnect when we connect.
- FInputPin : IMemInputPin;
- public
- constructor Create(ObjectName: string; Filter: TBCBaseFilter; Lock: TBCCritSec;
- out hr: HRESULT; const Name: WideString);
- // override CompleteConnect() so we can negotiate an allocator
- function CompleteConnect(ReceivePin: IPin): HRESULT; override;
- // negotiate the allocator and its buffer size/count and other properties
- // Calls DecideBufferSize to set properties
- function DecideAllocator(Pin: IMemInputPin; out Alloc: IMemAllocator): HRESULT; virtual;
- // override this to set the buffer size and count. Return an error
- // if the size/count is not to your liking.
- // The allocator properties passed in are those requested by the
- // input pin - use eg the alignment and prefix members if you have
- // no preference on these.
- function DecideBufferSize(Alloc: IMemAllocator; propInputRequest: PAllocatorProperties): HRESULT; virtual;
- // returns an empty sample buffer from the allocator
- function GetDeliveryBuffer(out Sample: IMediaSample; StartTime: PReferenceTime;
- EndTime: PReferenceTime; Flags: Longword): HRESULT; virtual;
- // deliver a filled-in sample to the connected input pin
- // note - you need to release it after calling this. The receiving
- // pin will addref the sample if it needs to hold it beyond the
- // call.
- function Deliver(Sample: IMediaSample): HRESULT; virtual;
- // override this to control the connection
- function InitAllocator(out Alloc: IMemAllocator): HRESULT; virtual;
- function CheckConnect(Pin: IPin): HRESULT; override;
- function BreakConnect: HRESULT; override;
- // override to call Commit and Decommit
- function Active: HRESULT; override;
- function Inactive: HRESULT; override;
- // we have a default handling of EndOfStream which is to return
- // an error, since this should be called on input pins only
- function EndOfStream: HRESULT; override; stdcall;
- // called from elsewhere in our filter to pass EOS downstream to
- // our connected input pin
- function DeliverEndOfStream: HRESULT; virtual;
- // same for Begin/EndFlush - we handle Begin/EndFlush since it
- // is an error on an output pin, and we have Deliver methods to
- // call the methods on the connected pin
- function BeginFlush: HRESULT; override; stdcall;
- function EndFlush: HRESULT; override; stdcall;
- function DeliverBeginFlush: HRESULT; virtual;
- function DeliverEndFlush: HRESULT; virtual;
- // deliver NewSegment to connected pin - you will need to
- // override this if you queue any data in your output pin.
- function DeliverNewSegment(Start, Stop: TReferenceTime; Rate: double): HRESULT; virtual;
- end;
- TBCBaseInputPin = class(TBCBasePin, IMemInputPin)
- protected
- FAllocator: IMemAllocator; // Default memory allocator
- // allocator is read-only, so received samples
- // cannot be modified (probably only relevant to in-place
- // transforms
- FReadOnly: boolean;
- //private: this should really be private... only the MPEG code
- // currently looks at it directly and it should use IsFlushing().
- // in flushing state (between BeginFlush and EndFlush)
- // if True, all Receives are returned with S_FALSE
- FFlushing: boolean;
- // Sample properties - initalized in Receive
- FSampleProps: TAMSample2Properties;
- public
- constructor Create(ObjectName: string; Filter: TBCBaseFilter;
- Lock: TBCCritSec; out hr: HRESULT; Name: WideString);
- destructor Destroy; override;
- // ----------IMemInputPin--------------
- // return the allocator interface that this input pin
- // would like the output pin to use
- function GetAllocator(out ppAllocator: IMemAllocator): HRESULT; stdcall;
- // tell the input pin which allocator the output pin is actually
- // going to use.
- function NotifyAllocator(pAllocator: IMemAllocator; bReadOnly: BOOL): HRESULT; stdcall;
- // this method is optional (can return E_NOTIMPL).
- // default implementation returns E_NOTIMPL. Override if you have
- // specific alignment or prefix needs, but could use an upstream
- // allocator
- function GetAllocatorRequirements(out pProps: TAllocatorProperties): HRESULT; stdcall;
- // do something with this media sample
- function Receive(pSample: IMediaSample): HRESULT; virtual; stdcall;
- // do something with these media samples
- function ReceiveMultiple(var pSamples: IMediaSample; nSamples: Longint;
- out nSamplesProcessed: Longint): HRESULT; stdcall;
- // See if Receive() blocks
- function ReceiveCanBlock: HRESULT; stdcall;
- //-----------Helper-------------
- // Default handling for BeginFlush - call at the beginning
- // of your implementation (makes sure that all Receive calls
- // fail). After calling this, you need to free any queued data
- // and then call downstream.
- function BeginFlush: HRESULT; override; stdcall;
- // default handling for EndFlush - call at end of your implementation
- // - before calling this, ensure that there is no queued data and no thread
- // pushing any more without a further receive, then call downstream,
- // then call this method to clear the m_bFlushing flag and re-enable
- // receives
- function EndFlush: HRESULT; override; stdcall;
- // Release the pin's allocator.
- function BreakConnect: HRESULT; override;
- // helper method to check the read-only flag
- property IsReadOnly: boolean read FReadOnly;
- // helper method to see if we are flushing
- property IsFlushing: boolean read FFlushing;
- // Override this for checking whether it's OK to process samples
- // Also call this from EndOfStream.
- function CheckStreaming: HRESULT; virtual;
- // Pass a Quality notification on to the appropriate sink
- function PassNotify(const q: TQuality): HRESULT;
- //================================================================================
- // IQualityControl methods (from CBasePin)
- //================================================================================
- function Notify(pSelf: IBaseFilter; q: TQuality): HRESULT; override; stdcall;
- // no need to override:
- // STDMETHODIMP SetSink(IQualityControl * piqc);
- // switch the pin to inactive state - may already be inactive
- function Inactive: HRESULT; override;
- // Return sample properties pointer
- function SampleProps: PAMSample2Properties;
- end;
- // milenko start (added TBCDynamicOutputPin conversion)
- TBLOCK_STATE = (NOT_BLOCKED, PENDING, BLOCKED);
- TBCDynamicOutputPin = class(TBCBaseOutputPin, IPinFlowControl)
- public
- constructor Create(ObjectName: WideString; Filter: TBCBaseFilter;
- Lock: TBCCritSec; out hr: HRESULT; Name: WideString);
- destructor Destroy; override;
- // IUnknown Methods
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult; override;
- // IPin Methods
- function Disconnect: HRESULT; override; stdcall;
- // IPinFlowControl Methods
- function Block(dwBlockFlags: DWORD; hEvent: THandle): HResult; stdcall;
- // Set graph config info
- procedure SetConfigInfo(GraphConfig: IGraphConfig; StopEvent: THandle);
- {$IFDEF DEBUG}
- function Deliver(Sample: IMediaSample): HRESULT; override;
- function DeliverEndOfStream: HRESULT; override;
- function DeliverNewSegment(Start, Stop: TReferenceTime; Rate: double): HRESULT; override;
- {$ENDIF} // DEBUG
- function DeliverBeginFlush: HRESULT; override;
- function DeliverEndFlush: HRESULT; override;
- function Active: HRESULT; override;
- function Inactive: HRESULT; override;
- function CompleteConnect(ReceivePin: IPin): HRESULT; override;
- function StartUsingOutputPin: HRESULT; virtual;
- procedure StopUsingOutputPin; virtual;
- function StreamingThreadUsingOutputPin: Boolean; virtual;
- function ChangeOutputFormat(const pmt: PAMMediaType; tSegmentStart, tSegmentStop:
- TreferenceTime; dSegmentRate: Double): HRESULT;
- function ChangeMediaType(const pmt: PAMMEdiaType): HRESULT;
- function DynamicReconnect(const pmt: PAMMediaType): HRESULT;
- protected
- // This lock should be held when the following class members are
- // being used: m_hNotifyCallerPinBlockedEvent, m_BlockState,
- // m_dwBlockCallerThreadID and m_dwNumOutstandingOutputPinUsers.
- FBlockStateLock: TBCCritSec;
- // This event should be signaled when the output pin is
- // not blocked. This is a manual reset event. For more
- // information on events, see the documentation for
- // CreateEvent() in the Windows SDK.
- FUnblockOutputPinEvent: THandle;
- // This event will be signaled when block operation succeedes or
- // when the user cancels the block operation. The block operation
- // can be canceled by calling IPinFlowControl2::Block( 0, NULL )
- // while the block operation is pending.
- FNotifyCallerPinBlockedEvent: THandle;
- // The state of the current block operation.
- FBlockState: TBLOCK_STATE;
- // The ID of the thread which last called IPinFlowControl::Block().
- // For more information on thread IDs, see the documentation for
- // GetCurrentThreadID() in the Windows SDK.
- FBlockCallerThreadID: DWORD;
- // The number of times StartUsingOutputPin() has been sucessfully
- // called and a corresponding call to StopUsingOutputPin() has not
- // been made. When this variable is greater than 0, the streaming
- // thread is calling IPin::NewSegment(), IPin::EndOfStream(),
- // IMemInputPin::Receive() or IMemInputPin::ReceiveMultiple(). The
- // streaming thread could also be calling: DynamicReconnect(),
- // ChangeMediaType() or ChangeOutputFormat(). The output pin cannot
- // be blocked while the output pin is being used.
- FNumOutstandingOutputPinUsers: DWORD;
- // This event should be set when the IMediaFilter::Stop() is called.
- // This is a manual reset event. It is also set when the output pin
- // delivers a flush to the connected input pin.
- FStopEvent: THandle;
- FGraphConfig: IGraphConfig;
- // TRUE if the output pin's allocator's samples are read only.
- // Otherwise FALSE. For more information, see the documentation
- // for IMemInputPin::NotifyAllocator().
- FPinUsesReadOnlyAllocator: Boolean;
- function SynchronousBlockOutputPin: HRESULT;
- function AsynchronousBlockOutputPin(NotifyCallerPinBlockedEvent: THandle): HRESULT;
- function UnblockOutputPin: HRESULT;
- procedure BlockOutputPin;
- procedure ResetBlockState;
- class function WaitEvent(Event: THandle): HRESULT;
- private
- function Initialize: HRESULT;
- function ChangeMediaTypeHelper(const pmt: PAMMediaType): HRESULT;
- {$IFDEF DEBUG}
- procedure AssertValid;
- {$ENDIF} // DEBUG
- end;
- // milenko end
- TBCTransformOutputPin = class;
- TBCTransformInputPin = class;
- TBCTransformFilter = class(TBCBaseFilter)
- protected
- FEOSDelivered : boolean; // have we sent EndOfStream
- FSampleSkipped : boolean; // Did we just skip a frame
- FQualityChanged: boolean; // Have we degraded?
- // critical section protecting filter state.
- FcsFilter: TBCCritSec;
- // critical section stopping state changes (ie Stop) while we're
- // processing a sample.
- //
- // This critical section is held when processing
- // events that occur on the receive thread - Receive() and EndOfStream().
- //
- // If you want to hold both m_csReceive and m_csFilter then grab
- // m_csFilter FIRST - like CTransformFilter::Stop() does.
- FcsReceive: TBCCritSec;
- // these hold our input and output pins
- FInput : TBCTransformInputPin;
- FOutput: TBCTransformOutputPin;
- public
- // map getpin/getpincount for base enum of pins to owner
- // override this to return more specialised pin objects
- function GetPinCount: integer; override;
- function GetPin(n: integer): TBCBasePin; override;
- function FindPin(Id: PWideChar; out ppPin: IPin): HRESULT; override; stdcall;
- // override state changes to allow derived transform filter
- // to control streaming start/stop
- function Stop: HRESULT; override; stdcall;
- function Pause: HRESULT; override; stdcall;
- constructor Create(ObjectName: string; unk: IUnKnown; const clsid: TGUID);
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
- destructor destroy; override;
- // =================================================================
- // ----- override these bits ---------------------------------------
- // =================================================================
- // These must be supplied in a derived class
- function Transform(msIn, msout: IMediaSample): HRESULT; virtual;
- // check if you can support mtIn
- function CheckInputType(mtIn: PAMMediaType): HRESULT; virtual; abstract;
- // check if you can support the transform from this input to this output
- function CheckTransform(mtIn, mtOut: PAMMediaType): HRESULT; virtual; abstract;
- // this goes in the factory template table to create new instances
- // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
- // call the SetProperties function with appropriate arguments
- function DecideBufferSize(Allocator: IMemAllocator; prop: PAllocatorProperties): HRESULT; virtual; abstract;
- // override to suggest OUTPUT pin media types
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; virtual; abstract;
- // =================================================================
- // ----- Optional Override Methods -----------------------
- // =================================================================
- // you can also override these if you want to know about streaming
- function StartStreaming: HRESULT; virtual;
- function StopStreaming: HRESULT; virtual;
- // override if you can do anything constructive with quality notifications
- function AlterQuality(const q: TQuality): HRESULT; virtual;
- // override this to know when the media type is actually set
- function SetMediaType(direction: TPinDirection; pmt: PAMMediaType): HRESULT; virtual;
- // chance to grab extra interfaces on connection
- function CheckConnect(dir: TPinDirection; Pin: IPin): HRESULT; virtual;
- function BreakConnect(dir: TPinDirection): HRESULT; virtual;
- function CompleteConnect(direction: TPinDirection; ReceivePin: IPin): HRESULT; virtual;
- // chance to customize the transform process
- function Receive(Sample: IMediaSample): HRESULT; virtual;
- // Standard setup for output sample
- function InitializeOutputSample(Sample: IMediaSample; out OutSample: IMediaSample): HRESULT; virtual;
- // if you override Receive, you may need to override these three too
- function EndOfStream: HRESULT; virtual;
- function BeginFlush: HRESULT; virtual;
- function EndFlush: HRESULT; virtual;
- function NewSegment(Start, Stop: TReferenceTime; Rate: double): HRESULT; virtual;
- property Input: TBCTransformInputPin read FInput write FInput;
- property Output: TBCTransformOutputPin read FOutPut write FOutput;
- end;
- TBCTransformInputPin = class(TBCBaseInputPin)
- private
- FTransformFilter: TBCTransformFilter;
- public
- constructor Create(ObjectName: string; TransformFilter: TBCTransformFilter;
- out hr: HRESULT; Name: WideString);
- destructor destroy; override;
- function QueryId(out id: PWideChar): HRESULT; override; stdcall;
- // Grab and release extra interfaces if required
- function CheckConnect(Pin: IPin): HRESULT; override;
- function BreakConnect: HRESULT; override;
- function CompleteConnect(ReceivePin: IPin): HRESULT; override;
- // check that we can support this output type
- function CheckMediaType(mtIn: PAMMediaType): HRESULT; override;
- // set the connection media type
- function SetMediaType(mt: PAMMediaType): HRESULT; override;
- // --- IMemInputPin -----
- // here's the next block of data from the stream.
- // AddRef it yourself if you need to hold it beyond the end
- // of this call.
- function Receive(pSample: IMediaSample): HRESULT; override; stdcall;
- // provide EndOfStream that passes straight downstream
- // (there is no queued data)
- function EndOfStream: HRESULT; override; stdcall;
- // passes it to CTransformFilter::BeginFlush
- function BeginFlush: HRESULT; override; stdcall;
- // passes it to CTransformFilter::EndFlush
- function EndFlush: HRESULT; override; stdcall;
- function NewSegment(Start, Stop: TReferenceTime; Rate: double): HRESULT; override; stdcall;
- // Check if it's OK to process samples
- function CheckStreaming: HRESULT; override;
- end;
- TBCTransformOutputPin = class(TBCBaseOutputPin)
- protected
- FTransformFilter: TBCTransformFilter;
- // implement IMediaPosition by passing upstream
- FPosition: IUnknown;
- public
- constructor Create(ObjectName: string; TransformFilter: TBCTransformFilter;
- out hr: HRESULT; Name: WideString);
- destructor destroy; override;
- // override to expose IMediaPosition
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult; override;
- // --- TBCBaseOutputPin ------------
- function QueryId(out Id: PWideChar): HRESULT; override; stdcall;
- // Grab and release extra interfaces if required
- function CheckConnect(Pin: IPin): HRESULT; override;
- function BreakConnect: HRESULT; override;
- function CompleteConnect(ReceivePin: IPin): HRESULT; override;
- // check that we can support this output type
- function CheckMediaType(mtOut: PAMMediaType): HRESULT; override;
- // set the connection media type
- function SetMediaType(pmt: PAMMediaType): HRESULT; override;
- // called from CBaseOutputPin during connection to ask for
- // the count and size of buffers we need.
- function DecideBufferSize(Alloc: IMemAllocator; Prop: PAllocatorProperties): HRESULT; override;
- // returns the preferred formats for a pin
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; override;
- // inherited from IQualityControl via CBasePin
- function Notify(Sendr: IBaseFilter; q: TQuality): HRESULT; override; stdcall;
- end;
- // milenko start (added TBCVideoTransformFilter conversion)
- TBCVideoTransformFilter = class(TBCTransformFilter)
- public
- constructor Create(Name: WideString; Unk: IUnknown; clsid: TGUID);
- destructor Destroy; override;
- function EndFlush: HRESULT; override;
- // =================================================================
- // ----- override these bits ---------------------------------------
- // =================================================================
- // The following methods are in CTransformFilter which is inherited.
- // They are mentioned here for completeness
- //
- // These MUST be supplied in a derived class
- //
- // NOTE:
- // virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut);
- // virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE;
- // virtual HRESULT CheckTransform
- // (const CMediaType* mtIn, const CMediaType* mtOut) PURE;
- // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
- // virtual HRESULT DecideBufferSize
- // (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE;
- // virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE;
- //
- // These MAY also be overridden
- //
- // virtual HRESULT StopStreaming();
- // virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt);
- // virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin);
- // virtual HRESULT BreakConnect(PIN_DIRECTION dir);
- // virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
- // virtual HRESULT EndOfStream(void);
- // virtual HRESULT BeginFlush(void);
- // virtual HRESULT EndFlush(void);
- // virtual HRESULT NewSegment
- // (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate);
- {$IFDEF PERF}
- // If you override this - ensure that you register all these ids
- // as well as any of your own,
- procedure RegisterPerfId; virtual;
- {$ENDIF}
- protected
- // =========== QUALITY MANAGEMENT IMPLEMENTATION ========================
- // Frames are assumed to come in three types:
- // Type 1: an AVI key frame or an MPEG I frame.
- // This frame can be decoded with no history.
- // Dropping this frame means that no further frame can be decoded
- // until the next type 1 frame.
- // Type 1 frames are sync points.
- // Type 2: an AVI non-key frame or an MPEG P frame.
- // This frame cannot be decoded unless the previous type 1 frame was
- // decoded and all type 2 frames since have been decoded.
- // Dropping this frame means that no further frame can be decoded
- // until the next type 1 frame.
- // Type 3: An MPEG B frame.
- // This frame cannot be decoded unless the previous type 1 or 2 frame
- // has been decoded AND the subsequent type 1 or 2 frame has also
- // been decoded. (This requires decoding the frames out of sequence).
- // Dropping this frame affects no other frames. This implementation
- // does not allow for these. All non-sync-point frames are treated
- // as being type 2.
- //
- // The spacing of frames of type 1 in a file is not guaranteed. There MUST
- // be a type 1 frame at (well, near) the start of the file in order to start
- // decoding at all. After that there could be one every half second or so,
- // there could be one at the start of each scene (aka "cut", "shot") or
- // there could be no more at all.
- // If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED
- // without losing all the rest of the movie. There is no way to tell whether
- // this is the case, so we find that we are in the gambling business.
- // To try to improve the odds, we record the greatest interval between type 1s
- // that we have seen and we bet on things being no worse than this in the
- // future.
- // You can tell if it's a type 1 frame by calling IsSyncPoint().
- // there is no architected way to test for a type 3, so you should override
- // the quality management here if you have B-frames.
- FKeyFramePeriod: integer; // the largest observed interval between type 1 frames
- // 1 means every frame is type 1, 2 means every other.
- FFramesSinceKeyFrame: integer; // Used to count frames since the last type 1.
- // becomes the new m_nKeyFramePeriod if greater.
- FSkipping: Boolean; // we are skipping to the next type 1 frame
- {$IFDEF PERF}
- FidFrameType: integer; // MSR id Frame type. 1=Key, 2="non-key"
- FidSkip: integer; // MSR id skipping
- FidLate: integer; // MSR id lateness
- FidTimeTillKey: integer; // MSR id for guessed time till next key frame.
- {$ENDIF}
- FitrLate: integer; // lateness from last Quality message
- // (this overflows at 214 secs late).
- FtDecodeStart: integer; // timeGetTime when decode started.
- FitrAvgDecode: integer; // Average decode time in reference units.
- FNoSkip: Boolean; // debug - no skipping.
- // We send an EC_QUALITY_CHANGE notification to the app if we have to degrade.
- // We send one when we start degrading, not one for every frame, this means
- // we track whether we've sent one yet.
- FQualityChanged: Boolean;
- // When non-zero, don't pass anything to renderer until next keyframe
- // If there are few keys, give up and eventually draw something
- FWaitForKey: integer;
- function AbortPlayback(hr: HRESULT): HRESULT; // if something bad happens
- function ShouldSkipFrame(pIn: IMediaSample): Boolean;
- public
- function StartStreaming: HRESULT; override;
- function Receive(Sample: IMediaSample): HRESULT; override;
- function AlterQuality(const q: TQuality): HRESULT; override;
- end;
- // milenko end
- TBCTransInPlaceOutputPin = class;
- TBCTransInPlaceInputPin = class;
- TBCTransInPlaceFilter = class(TBCTransformFilter)
- public
- // map getpin/getpincount for base enum of pins to owner
- // override this to return more specialised pin objects
- function GetPin(n: integer): TBCBasePin; override;
- // Set bModifiesData == false if your derived filter does
- // not modify the data samples (for instance it's just copying
- // them somewhere else or looking at the timestamps).
- constructor Create(ObjectName: string; unk: IUnKnown; clsid: TGUID;
- out hr: HRESULT; ModifiesData: boolean = True);
- constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
- // The following are defined to avoid undefined pure virtuals.
- // Even if they are never called, they will give linkage warnings/errors
- // We override EnumMediaTypes to bypass the transform class enumerator
- // which would otherwise call this.
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; override;
- // This is called when we actually have to provide out own allocator.
- function DecideBufferSize(Alloc: IMemAllocator; propInputRequest: PAllocatorProperties): HRESULT; override;
- // The functions which call this in CTransform are overridden in this
- // class to call CheckInputType with the assumption that the type
- // does not change. In Debug builds some calls will be made and
- // we just ensure that they do not assert.
- function CheckTransform(mtIn, mtOut: PAMMediaType): HRESULT; override;
- // =================================================================
- // ----- You may want to override this -----------------------------
- // =================================================================
- function CompleteConnect(dir: TPinDirection; ReceivePin: IPin): HRESULT; override;
- // chance to customize the transform process
- function Receive(Sample: IMediaSample): HRESULT; override;
- // =================================================================
- // ----- You MUST override these -----------------------------------
- // =================================================================
- function Transform(Sample: IMediaSample): HRESULT; reintroduce; virtual; abstract;
- // this goes in the factory template table to create new instances
- // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *);
- protected
- FModifiesData: boolean; // Does this filter change the data?
- function Copy(Source: IMediaSample): IMediaSample;
- // these hold our input and output pins
- function InputPin: TBCTransInPlaceInputPin;
- function OutputPin: TBCTransInPlaceOutputPin;
- // Helper to see if the input and output types match
- function TypesMatch: boolean;
- // Are the input and output allocators different?
- function UsingDifferentAllocators: boolean;
- end;
- TBCTransInPlaceInputPin = class(TBCTransformInputPin)
- protected
- FTIPFilter: TBCTransInPlaceFilter; // our filter
- FReadOnly : boolean; // incoming stream is read only
- public
- constructor Create(ObjectName: string; Filter: TBCTransInPlaceFilter;
- out hr: HRESULT; Name: WideString);
- // --- IMemInputPin -----
- // Provide an enumerator for media types by getting one from downstream
- function EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT; override; stdcall;
- // Say whether media type is acceptable.
- function CheckMediaType(pmt: PAMMediaType): HRESULT; override;
- // Return our upstream allocator
- function GetAllocator(out Allocator: IMemAllocator): HRESULT; stdcall;
- // get told which allocator the upstream output pin is actually
- // going to use.
- function NotifyAllocator(Allocator: IMemAllocator; ReadOnly: BOOL): HRESULT; stdcall;
- // Allow the filter to see what allocator we have
- // N.B. This does NOT AddRef
- function PeekAllocator: IMemAllocator;
- // Pass this on downstream if it ever gets called.
- function GetAllocatorRequirements(props: PAllocatorProperties): HRESULT; stdcall;
- property ReadOnly: Boolean read FReadOnly;
- end;
- // ==================================================
- // Implements the output pin
- // ==================================================
- TBCTransInPlaceOutputPin = class(TBCTransformOutputPin)
- protected
- // m_pFilter points to our CBaseFilter
- FTIPFilter: TBCTransInPlaceFilter;
- public
- constructor Create(ObjectName: string; Filter: TBCTransInPlaceFilter;
- out hr: HRESULT; Name: WideString);
- // --- CBaseOutputPin ------------
- // negotiate the allocator and its buffer size/count
- // Insists on using our own allocator. (Actually the one upstream of us).
- // We don't override this - instead we just agree the default
- // then let the upstream filter decide for itself on reconnect
- // virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc);
- // Provide a media type enumerator. Get it from upstream.
- function EnumMediaTypes(out ppEnum: IEnumMediaTypes): HRESULT; override; stdcall;
- // Say whether media type is acceptable.
- function CheckMediaType(pmt: PAMMediaType): HRESULT; override;
- // This just saves the allocator being used on the output pin
- // Also called by input pin's GetAllocator()
- procedure SetAllocator(Allocator: IMemAllocator);
- function ConnectedIMemInputPin: IMemInputPin;
- // Allow the filter to see what allocator we have
- // N.B. This does NOT AddRef
- function PeekAllocator: IMemAllocator;
- end;
- TBCBasePropertyPage = class(TBCUnknown, IPropertyPage)
- private
- FObjectSet: boolean; // SetObject has been called or not.
- protected
- FPageSite: IPropertyPageSite; // Details for our property site
- FDirty: boolean; // Has anything been changed
- FForm: TFormPropertyPage;
- public
- constructor Create(Name: String; Unk: IUnKnown; Form: TFormPropertyPage);
- destructor Destroy; override;
- procedure SetPageDirty;
- { IPropertyPage }
- function SetPageSite(const pageSite: IPropertyPageSite): HResult; stdcall;
- function Activate(hwndParent: HWnd; const rc: TRect; bModal: BOOL): HResult; stdcall;
- function Deactivate: HResult; stdcall;
- function GetPageInfo(out pageInfo: TPropPageInfo): HResult; stdcall;
- function SetObjects(cObjects: Longint; pUnkList: PUnknownList): HResult; stdcall;
- function Show(nCmdShow: Integer): HResult; stdcall;
- function Move(const rect: TRect): HResult; stdcall;
- function IsPageDirty: HResult; stdcall;
- function Apply: HResult; stdcall;
- function Help(pszHelpDir: POleStr): HResult; stdcall;
- function TranslateAccelerator(msg: PMsg): HResult; stdcall;
- end;
- TOnConnect = procedure(sender: Tobject; Unknown: IUnknown) of object;
- TFormPropertyPage = class(TForm, IUnKnown, IPropertyPage)
- private
- FPropertyPage: TBCBasePropertyPage;
- procedure MyWndProc(var aMsg: TMessage);
- public
- constructor Create(AOwner: TComponent); override;
- published
- function OnConnect(Unknown: IUnknown): HRESULT; virtual;
- function OnDisconnect: HRESULT; virtual;
- function OnApplyChanges: HRESULT; virtual;
- property PropertyPage : TBCBasePropertyPage read FPropertyPage implements IUnKnown, IPropertyPage;
- end;
- TBCBaseDispatch = class{IDispatch}
- protected
- FTI: ITypeInfo;
- public
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(const iid: TGUID; info: Cardinal; lcid: LCID; out tinfo): HRESULT; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- end;
- TBCMediaControl = class(TBCUnknown, IDispatch)
- public
- FBaseDisp: TBCBaseDispatch;
- constructor Create(name: string; unk: IUnknown);
- destructor Destroy; override;
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
- end;
- TBCMediaEvent = class(TBCUnknown, IDisPatch{,IMediaEventEx})
- protected
- FBasedisp: TBCBaseDispatch;
- public
- constructor Create(Name: string; Unk: IUnknown);
- destructor destroy; override;
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
- end;
- TBCMediaPosition = class(TBCUnknown, IDispatch {IMediaPosition})
- protected
- FBaseDisp: TBCBaseDispatch;
- public
- constructor Create(Name: String; Unk: IUnknown); overload;
- constructor Create(Name: String; Unk: IUnknown; out hr: HRESULT); overload;
- destructor Destroy; override;
- // IDispatch methods
- function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
- function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
- function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
- end;
- // A utility class that handles IMediaPosition and IMediaSeeking on behalf
- // of single-input pin renderers, or transform filters.
- //
- // Renderers will expose this from the filter; transform filters will
- // expose it from the output pin and not the renderer.
- //
- // Create one of these, giving it your IPin* for your input pin, and delegate
- // all IMediaPosition methods to it. It will query the input pin for
- // IMediaPosition and respond appropriately.
- //
- // Call ForceRefresh if the pin connection changes.
- //
- // This class no longer caches the upstream IMediaPosition or IMediaSeeking
- // it acquires it on each method call. This means ForceRefresh is not needed.
- // The method is kept for source compatibility and to minimise the changes
- // if we need to put it back later for performance reasons.
- TBCPosPassThru = class(TBCMediaPosition, IMediaSeeking)
- protected
- FPin: IPin;
- function GetPeer(out MP: IMediaPosition): HRESULT;
- function GetPeerSeeking(out MS: IMediaSeeking): HRESULT;
- public
- constructor Create(name: String; Unk: IUnknown; out hr: HRESULT; Pin: IPin);
- function ForceRefresh: HRESULT;{return S_OK;}
- // override to return an accurate current position
- function GetMediaTime(out StartTime, EndTime: int64): HRESULT; virtual;
- // IMediaSeeking methods
- function GetCapabilities(out pCapabilities: DWORD): HRESULT; stdcall;
- function CheckCapabilities(var pCapabilities: DWORD): HRESULT; stdcall;
- function IsFormatSupported(const pFormat: TGUID): HRESULT; stdcall;
- function QueryPreferredFormat(out pFormat: TGUID): HRESULT; stdcall;
- function GetTimeFormat(out pFormat: TGUID): HRESULT; stdcall;
- function IsUsingTimeFormat(const pFormat: TGUID): HRESULT; stdcall;
- function SetTimeFormat(const pFormat: TGUID): HRESULT; stdcall;
- function GetDuration(out pDuration: int64): HRESULT; stdcall;
- function GetStopPosition(out pStop: int64): HRESULT; stdcall;
- function GetCurrentPosition(out pCurrent: int64): HRESULT; stdcall;
- function ConvertTimeFormat(out pTarget: int64; pTargetFormat: PGUID;
- Source: int64; pSourceFormat: PGUID): HRESULT; stdcall;
- function SetPositions(var pCurrent: int64; dwCurrentFlags: DWORD;
- var pStop: int64; dwStopFlags: DWORD): HRESULT; stdcall;
- function GetPositions(out pCurrent, pStop: int64): HRESULT; stdcall;
- function GetAvailable(out pEarliest, pLatest: int64): HRESULT; stdcall;
- function SetRate(dRate: double): HRESULT; stdcall;
- function GetRate(out pdRate: double): HRESULT; stdcall;
- function GetPreroll(out pllPreroll: int64): HRESULT; stdcall;
- // IMediaPosition properties
- function get_Duration(out plength: TRefTime): HResult; stdcall;
- function put_CurrentPosition(llTime: TRefTime): HResult; stdcall;
- function get_CurrentPosition(out pllTime: TRefTime): HResult; stdcall;
- function get_StopTime(out pllTime: TRefTime): HResult; stdcall;
- function put_StopTime(llTime: TRefTime): HResult; stdcall;
- function get_PrerollTime(out pllTime: TRefTime): HResult; stdcall;
- function put_PrerollTime(llTime: TRefTime): HResult; stdcall;
- function put_Rate(dRate: double): HResult; stdcall;
- function get_Rate(out pdRate: double): HResult; stdcall;
- function CanSeekForward(out pCanSeekForward: Longint): HResult; stdcall;
- function CanSeekBackward(out pCanSeekBackward: Longint): HResult; stdcall;
- end;
- TBCRendererPosPassThru = class(TBCPosPassThru)
- protected
- FPositionLock: TBCCritSec; // Locks access to our position
- FStartMedia : Int64; // Start media time last seen
- FEndMedia : Int64; // And likewise the end media
- FReset : boolean; // Have media times been set
- public
- // Used to help with passing media times through graph
- constructor Create(name: String; Unk: IUnknown; out hr: HRESULT; Pin: IPin); reintroduce;
- destructor destroy; override;
- function RegisterMediaTime(MediaSample: IMediaSample): HRESULT; overload;
- function RegisterMediaTime(StartTime, EndTime: int64): HRESULT; overload;
- function GetMediaTime(out StartTime, EndTime: int64): HRESULT; override;
- function ResetMediaTime: HRESULT;
- function EOS: HRESULT;
- end;
- // wrapper for event objects
- TBCAMEvent = class
- protected
- FEvent: THANDLE;
- public
- constructor Create(ManualReset: boolean = false);
- destructor destroy; override;
- property Handle: THandle read FEvent;
- procedure SetEv;
- function Wait(Timeout: Cardinal = INFINITE): boolean;
- procedure Reset;
- function Check: boolean;
- end;
- TBCTimeoutEvent = TBCAMEvent;
- // wrapper for event objects that do message processing
- // This adds ONE method to the CAMEvent object to allow sent
- // messages to be processed while waiting
- TBCAMMsgEvent = class(TBCAMEvent)
- public
- // Allow SEND messages to be processed while waiting
- function WaitMsg(Timeout: DWord = INFINITE): boolean;
- end;
- // support for a worker thread
- // simple thread class supports creation of worker thread, synchronization
- // and communication. Can be derived to simplify parameter passing
- TThreadProc = function: DWORD of object;
- TBCAMThread = class
- private
- FEventSend: TBCAMEvent;
- FEventComplete: TBCAMEvent;
- FParam: DWord;
- FReturnVal: DWord;
- FThreadProc: TThreadProc;
- protected
- FThread: THandle;
- // thread will run this function on startup
- // must be supplied by derived class
- function ThreadProc: DWord; virtual;
- public
- FAccessLock: TBCCritSec; // locks access by client threads
- FWorkerLock: TBCCritSec; // locks access to shared objects
- constructor Create;
- destructor Destroy; override;
- // thread initially runs this. param is actually 'this'. function
- // just gets this and calls ThreadProc
- function InitialThreadProc(p: Pointer): DWORD; virtual; stdcall; // WINAPI;
- // start thread running - error if already running
- function Create_: boolean;
- // signal the thread, and block for a response
- //
- function CallWorker(Param: DWORD): DWORD;
- // accessor thread calls this when done with thread (having told thread
- // to exit)
- procedure Close;
- // ThreadExists
- // Return True if the thread exists. FALSE otherwise
- function ThreadExists: boolean; // const
- // wait for the next request
- function GetRequest: DWORD;
- // is there a request?
- function CheckRequest(Param: PDWORD): boolean;
- // reply to the request
- procedure Reply(v: DWORD);
- // If you want to do WaitForMultipleObjects you'll need to include
- // this handle in your wait list or you won't be responsive
- function GetRequestHandle: THANDLE;
- // Find out what the request was
- function GetRequestParam: DWORD;
- // call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if
- // available. S_FALSE means it's not available.
- class function CoInitializeHelper: HRESULT;
- end;
- TBCRenderedInputPin = class(TBCBaseInputPin)
- private
- procedure DoCompleteHandling;
- protected
- // Member variables to track state
- FAtEndOfStream : boolean; // Set by EndOfStream
- FCompleteNotified : boolean; // Set when we notify for EC_COMPLETE
- public
- constructor Create(ObjectName: string; Filter: TBCBaseFilter;
- Lock: TBCCritSec; out hr: HRESULT; Name: WideString);
- // Override methods to track end of stream state
- function EndOfStream: HRESULT; override; stdcall;
- function EndFlush: HRESULT; override; stdcall;
- function Active: HRESULT; override;
- function Run(Start: TReferenceTime): HRESULT; override;
- end;
- (* A generic list of pointers to objects.
- No storage management or copying is done on the objects pointed to.
- Objectives: avoid using MFC libraries in ndm kernel mode and
- provide a really useful list type.
- The class is thread safe in that separate threads may add and
- delete items in the list concurrently although the application
- must ensure that constructor and destructor access is suitably
- synchronised. An application can cause deadlock with operations
- which use two lists by simultaneously calling
- list1->Operation(list2) and list2->Operation(list1). So don't!
- The names must not conflict with MFC classes as an application
- may use both.
- *)
- (* A POSITION represents (in some fashion that's opaque) a cursor
- on the list that can be set to identify any element. NULL is
- a valid value and several operations regard NULL as the position
- "one step off the end of the list". (In an n element list there
- are n+1 places to insert and NULL is that "n+1-th" value).
- The POSITION of an element in the list is only invalidated if
- that element is deleted. Move operations may mean that what
- was a valid POSITION in one list is now a valid POSITION in
- a different list.
- Some operations which at first sight are illegal are allowed as
- harmless no-ops. For instance RemoveHead is legal on an empty
- list and it returns NULL. This allows an atomic way to test if
- there is an element there, and if so, get it. The two operations
- AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper).
- Single element operations return POSITIONs, non-NULL means it worked.
- whole list operations return a BOOL. True means it all worked.
- This definition is the same as the POSITION type for MFCs, so we must
- avoid defining it twice.
- *)
- Position = Pointer;
- {$ifdef DEBUG}
- TBCNode = class(TBCBaseObject)
- {$else}
- TBCNode = class
- {$endif}
- private
- FPrev: TBCNode; // Previous node in the list
- FNext: TBCNode; // Next node in the list
- FObject: Pointer; // Pointer to the object
- public
- // Constructor - initialise the object's pointers
- {$ifdef DEBUG}
- constructor Create;
- {$endif}
- // Return the previous node before this one
- property Prev: TBCNode read FPrev write FPrev;
- // Return the next node after this one
- property Next: TBCNode read FNext write FNext;
- // Get the pointer to the object for this node */
- property Data: Pointer read FObject write FObject;
- end;
- TBCNodeCache = class
- private
- FCacheSize: Integer;
- FUsed: Integer;
- FHead: TBCNode;
- public
- constructor Create(CacheSize: Integer);
- destructor Destroy; override;
- procedure AddToCache(Node: TBCNode);
- function RemoveFromCache: TBCNode;
- end;
- (* A class representing one node in a list.
- Each node knows a pointer to it's adjacent nodes and also a pointer
- to the object that it looks after.
- All of these pointers can be retrieved or set through member functions.
- *)
- TBCBaseList = class
- {$ifdef DEBUG}
- (TBCBaseObject)
- {$endif}
- (* Making these classes inherit from CBaseObject does nothing
- functionally but it allows us to check there are no memory
- leaks in debug builds.
- *)
- protected
- FFirst: TBCNode; // Pointer to first node in the list
- FLast: TBCNode; // Pointer to the last node in the list
- FCount: LongInt; // Number of nodes currently in the list
- private
- FCache: TBCNodeCache; // Cache of unused node pointers
- public
- constructor Create(Name: string; Items: Integer = DEFAULTCACHE);
- destructor Destroy; override;
- // Remove all the nodes from self i.e. make the list empty
- procedure RemoveAll;
- // Return a cursor which identifies the first element of self
- function GetHeadPositionI: Position;
- /// Return a cursor which identifies the last element of self
- function GetTailPositionI: Position;
- // Return the number of objects in self
- function GetCountI: Integer;
- protected
- (* Return the pointer to the object at rp,
- Update rp to the next node in self
- but make it nil if it was at the end of self.
- This is a wart retained for backwards compatibility.
- GetPrev is not implemented.
- Use Next, Prev and Get separately.
- *)
- function GetNextI(var rp: Position): Pointer;
- (* Return a pointer to the object at p
- Asking for the object at nil will return nil harmlessly.
- *)
- function GetI(p: Position): Pointer;
- public
- (* return the next / prev position in self
- return NULL when going past the end/start.
- Next(nil) is same as GetHeadPosition()
- Prev(nil) is same as GetTailPosition()
- An n element list therefore behaves like a n+1 element
- cycle with nil at the start/end.
- !!WARNING!! - This handling of nil is DIFFERENT from GetNext.
- Some reasons are:
- 1. For a list of n items there are n+1 positions to insert
- These are conveniently encoded as the n POSITIONs and nil.
- 2. If you are keeping a list sorted (fairly common) and you
- search forward for an element to insert before and don't
- find it you finish up with nil as the element before which
- to insert. You then want that nil to be a valid POSITION
- so that you can insert before it and you want that insertion
- point to mean the (n+1)-th one that doesn't have a POSITION.
- (symmetrically if you are working backwards through the list).
- 3. It simplifies the algebra which the methods generate.
- e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x)
- in ALL cases. All the other arguments probably are reflections
- of the algebraic point.
- *)
- function Next(pos: Position): Position;
- function Prev(pos: Position): Position;
- (* Return the first position in self which holds the given
- pointer. Return nil if the pointer was not not found.
- *)
- protected
- function FindI(Obj: Pointer): Position;
- (* Remove the first node in self (deletes the pointer to its
- object from the list, does not free the object itself).
- Return the pointer to its object.
- If self was already empty it will harmlessly return nil.
- *)
- function RemoveHeadI: Pointer;
- (* Remove the last node in self (deletes the pointer to its
- object from the list, does not free the object itself).
- Return the pointer to its object.
- If self was already empty it will harmlessly return nil.
- *)
- function RemoveTailI: Pointer;
- (* Remove the node identified by p from the list (deletes the pointer
- to its object from the list, does not free the object itself).
- Asking to Remove the object at nil will harmlessly return nil.
- Return the pointer to the object removed.
- *)
- function RemoveI(pos: Position): Pointer;
- (* Add single object *pObj to become a new last element of the list.
- Return the new tail position, nil if it fails.
- If you are adding a COM objects, you might want AddRef it first.
- Other existing POSITIONs in self are still valid
- *)
- function AddTailI(Obj: Pointer): Position;
- public
- (* Add all the elements in *pList to the tail of self.
- This duplicates all the nodes in *pList (i.e. duplicates
- all its pointers to objects). It does not duplicate the objects.
- If you are adding a list of pointers to a COM object into the list
- it's a good idea to AddRef them all it when you AddTail it.
- Return True if it all worked, FALSE if it didn't.
- If it fails some elements may have been added.
- Existing POSITIONs in self are still valid
- If you actually want to MOVE the elements, use MoveToTail instead.
- *)
- function AddTail(List: TBCBaseList): boolean;
- // Mirror images of AddHead:
- (* Add single object to become a new first element of the list.
- Return the new head position, nil if it fails.
- Existing POSITIONs in self are still valid
- *)
- protected
- function AddHeadI(Obj: Pointer): Position;
- public
- (* Add all the elements in *pList to the head of self.
- Same warnings apply as for AddTail.
- Return True if it all worked, FALSE if it didn't.
- If it fails some of the objects may have been added.
- If you actually want to MOVE the elements, use MoveToHead instead.
- *)
- function AddHead(List: TBCBaseList): BOOL;
- (* Add the object *pObj to self after position p in self.
- AddAfter(nil,x) adds x to the start - equivalent to AddHead
- Return the position of the object added, nil if it failed.
- Existing POSITIONs in self are undisturbed, including p.
- *)
- protected
- function AddAfterI(pos: Position; Obj: Pointer): Position;
- public
- (* Add the list *pList to self after position p in self
- AddAfter(nil,x) adds x to the start - equivalent to AddHead
- Return True if it all worked, FALSE if it didn't.
- If it fails, some of the objects may be added
- Existing POSITIONs in self are undisturbed, including p.
- *)
- function AddAfter(p: Position; List: TBCBaseList): BOOL;
- (* Mirror images:
- Add the object *pObj to this-List after position p in self.
- AddBefore(nil,x) adds x to the end - equivalent to AddTail
- Return the position of the new object, nil if it fails
- Existing POSITIONs in self are undisturbed, including p.
- *)
- protected
- function AddBeforeI(pos: Position; Obj: Pointer): Position;
- public
- (* Add the list *pList to self before position p in self
- AddAfter(nil,x) adds x to the start - equivalent to AddHead
- Return True if it all worked, FALSE if it didn't.
- If it fails, some of the objects may be added
- Existing POSITIONs in self are undisturbed, including p.
- *)
- function AddBefore(p: Position; List: TBCBaseList): BOOL;
- (* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x)
- even in cases where p is nil or Next(p) is nil.
- Similarly for mirror images etc.
- This may make it easier to argue about programs.
- *)
- (* The following operations do not copy any elements.
- They move existing blocks of elements around by switching pointers.
- They are fairly efficient for long lists as for short lists.
- (Alas, the Count slows things down).
- They split the list into two parts.
- One part remains as the original list, the other part
- is appended to the second list. There are eight possible
- variations:
- Split the list {after/before} a given element
- keep the {head/tail} portion in the original list
- append the rest to the {head/tail} of the new list.
- Since After is strictly equivalent to Before Next
- we are not in serious need of the Before/After variants.
- That leaves only four.
- If you are processing a list left to right and dumping
- the bits that you have processed into another list as
- you go, the Tail/Tail variant gives the most natural result.
- If you are processing in reverse order, Head/Head is best.
- By using nil positions and empty lists judiciously either
- of the other two can be built up in two operations.
- The definition of nil (see Next/Prev etc) means that
- degenerate cases include
- "move all elements to new list"
- "Split a list into two lists"
- "Concatenate two lists"
- (and quite a few no-ops)
- !!WARNING!! The type checking won't buy you much if you get list
- positions muddled up - e.g. use a POSITION that's in a different
- list and see what a mess you get!
- *)
- (* Split self after position p in self
- Retain as self the tail portion of the original self
- Add the head portion to the tail end of *pList
- Return True if it all worked, FALSE if it didn't.
- e.g.
- foo->MoveToTail(foo->GetHeadPosition(), bar);
- moves one element from the head of foo to the tail of bar
- foo->MoveToTail(nil, bar);
- is a no-op, returns nil
- foo->MoveToTail(foo->GetTailPosition, bar);
- concatenates foo onto the end of bar and empties foo.
- A better, except excessively long name might be
- MoveElementsFromHeadThroughPositionToOtherTail
- *)
- function MoveToTail(pos: Position; List: TBCBaseList): boolean;
- (* Mirror image:
- Split self before position p in self.
- Retain in self the head portion of the original self
- Add the tail portion to the start (i.e. head) of *pList
- e.g.
- foo->MoveToHead(foo->GetTailPosition(), bar);
- moves one element from the tail of foo to the head of bar
- foo->MoveToHead(nil, bar);
- is a no-op, returns nil
- foo->MoveToHead(foo->GetHeadPosition, bar);
- concatenates foo onto the start of bar and empties foo.
- *)
- function MoveToHead(pos: Position; List: TBCBaseList): boolean;
- (* Reverse the order of the [pointers to] objects in self *)
- procedure Reverse;
- end;
- // Desc: DirectShow base classes - defines classes to simplify creation of
- // ActiveX source filters that support continuous generation of data.
- // No support is provided for IMediaControl or IMediaPosition.
- //
- // Derive your source filter from CSource.
- // During construction either:
- // Create some CSourceStream objects to manage your pins
- // Provide the user with a means of doing so eg, an IPersistFile interface.
- //
- // CSource provides:
- // IBaseFilter interface management
- // IMediaFilter interface management, via CBaseFilter
- // Pin counting for CBaseFilter
- //
- // Derive a class from CSourceStream to manage your output pin types
- // Implement GetMediaType/1 to return the type you support. If you support multiple
- // types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount.
- // Implement Fillbuffer() to put data into one buffer.
- //
- // CSourceStream provides:
- // IPin management via CBaseOutputPin
- // Worker thread management
- // Override construction to provide a means of creating
- // CSourceStream derived objects - ie a way of creating pins.
- TBCSourceStream = class;
- TStreamArray = array of TBCSourceStream;
- TBCSource = class(TBCBaseFilter)
- protected
- FPins: Integer; // The number of pins on this filter. Updated by CSourceStream
- FStreams: Pointer; // the pins on this filter.
- FStateLock: TBCCritSec;
- public
- constructor Create(const Name: string; unk: IUnknown; const clsid: TGUID; out hr: HRESULT); overload;
- constructor Create(const Name: string; unk: IUnknown; const clsid: TGUID); overload;
- destructor Destroy; override;
- function GetPinCount: Integer; override;
- function GetPin(n: Integer): TBCBasePin; override;
- // -- Utilities --
- property StateLock: TBCCritSec read FStateLock; // provide our critical section
- function AddPin(Stream: TBCSourceStream): HRESULT;
- function RemovePin(Stream: TBCSourceStream): HRESULT;
- function FindPin(Id: PWideChar; out Pin: IPin): HRESULT; override;
- function FindPinNumber(Pin: IPin): Integer;
- end;
- //
- // CSourceStream
- //
- // Use this class to manage a stream of data that comes from a
- // pin.
- // Uses a worker thread to put data on the pin.
- TThreadCommand = (
- CMD_INIT,
- CMD_PAUSE,
- CMD_RUN,
- CMD_STOP,
- CMD_EXIT
- );
- TBCSourceStream = class(TBCBaseOutputPin)
- public
- constructor Create(const ObjectName: string; out hr: HRESULT;
- Filter: TBCSource; const Name: WideString);
- destructor Destroy; override;
- protected
- FThread: TBCAMThread;
- FFilter: TBCSource; // The parent of this stream
- // *
- // * Data Source
- // *
- // * The following three functions: FillBuffer, OnThreadCreate/Destroy, are
- // * called from within the ThreadProc. They are used in the creation of
- // * the media samples this pin will provide
- // *
- // Override this to provide the worker thread a means
- // of processing a buffer
- function FillBuffer(Samp: IMediaSample): HRESULT; virtual; abstract;
- // Called as the thread is created/destroyed - use to perform
- // jobs such as start/stop streaming mode
- // If OnThreadCreate returns an error the thread will exit.
- function OnThreadCreate: HRESULT; virtual;
- function OnThreadDestroy: HRESULT; virtual;
- function OnThreadStartPlay: HRESULT; virtual;
- public
- // *
- // * Worker Thread
- // *
- function Active: HRESULT; override; // Starts up the worker thread
- function Inactive: HRESULT; override; // Exits the worker thread.
- // thread commands
- function Init: HRESULT;
- function Exit_: HRESULT;
- function Run: HRESULT; reintroduce;
- function Pause: HRESULT;
- function Stop: HRESULT;
- // *
- // * AM_MEDIA_TYPE support
- // *
- // If you support more than one media type then override these 2 functions
- function CheckMediaType(MediaType: PAMMediaType): HRESULT; override;
- function GetMediaType(Position: integer; out MediaType: PAMMediaType): HRESULT; overload; override; // List pos. 0-n
- // If you support only one type then override this fn.
- // This will only be called by the default implementations
- // of CheckMediaType and GetMediaType(int, CMediaType*)
- // You must override this fn. or the above 2!
- function GetMediaType(MediaType: PAMMediaType): HRESULT; reintroduce; overload; virtual;
- function QueryId(out id: PWideChar): HRESULT; override;
- protected
- function GetRequest: TThreadCommand;
- function CheckRequest(var com: TThreadCommand): boolean;
- // override these if you want to add thread commands
- function ThreadProc: DWORD; virtual; // the thread function
- function DoBufferProcessingLoop: HRESULT; virtual; // the loop executed whilst running
- end;
- TBCBaseRenderer = class;
- TBCRendererInputPin = class;
- // This is our input pin class that channels calls to the renderer
- TBCRendererInputPin = class(TBCBaseInputPin)
- protected
- FRenderer: TBCBaseRenderer;
- public
- constructor Create(Renderer: TBCBaseRenderer; out hr: HResult;
- Name: PWideChar);
- // Overriden from the base pin classes
- function BreakConnect: HResult; override;
- function CompleteConnect(ReceivePin: IPin): HResult; override;
- function SetMediaType(MediaType: PAMMediaType): HResult; override;
- function CheckMediaType(MediaType: PAMMediaType): HResult; override;
- function Active: HResult; override;
- function Inactive: HResult; override;
- // Add rendering behaviour to interface functions
- function QueryId(out Id: PWideChar): HResult; override; stdcall;
- function EndOfStream: HResult; override; stdcall;
- function BeginFlush: HResult; override; stdcall;
- function EndFlush: HResult; override; stdcall;
- function Receive(MediaSample: IMediaSample): HResult; override; stdcall;
- function InheritedReceive(MediaSample: IMediaSample): HResult;
- virtual; stdcall;
- end;
- // Main renderer class that handles synchronisation and state changes
- TBCBaseRenderer = class(TBCBaseFilter)
- protected
- // friend class CRendererInputPin;
- //FEndOfStreamTimerCB: TFNTimeCallBack;
- // Media seeking pass by object
- FPosition: TBCRendererPosPassThru;
- //FPosition: IUnknown;
- // Used to signal timer events
- FRenderEvent: TBCAMEvent;
- // Signalled to release worker thread
- FThreadSignal: TBCAMEvent;
- // Signalled when state complete
- FCompleteEvent: TBCAMEvent;
- // Stop us from rendering more data
- FAbort: Boolean;
- // Are we currently streaming
- FIsStreaming: Boolean;
- // Timer advise cookie
- FAdvisedCookie: DWord;
- // Current image media sample
- FMediaSample: IMediaSample;
- // Any more samples in the stream
- FIsEOS: Boolean;
- // Have we delivered an EC_COMPLETE
- FIsEOSDelivered: Boolean;
- // Our renderer input pin object
- FInputPin: TBCRendererInputPin;
- // Critical section for interfaces
- FInterfaceLock: TBCCritSec;
- // Controls access to internals
- FRendererLock: TBCCritSec;
- // QualityControl sink
- FQSink: IQualityControl;
- // Can we signal an EC_REPAINT
- FRepaintStatus: Boolean;
- // Avoid some deadlocks by tracking filter during stop
- // Inside Receive between PrepareReceive and actually processing the sample
- FInReceive: Boolean;
- // Time when we signal EC_COMPLETE
- FSignalTime: TReferenceTime;
- // Used to signal end of stream
- FEndOfStreamTimer: DWord;
- // This lock protects the creation and of FPosition and FInputPin.
- // It ensures that two threads cannot create either object simultaneously.
- FObjectCreationLock: TBCCritSec;
- // Milenko start (must be outside of the class and with stdcall; or it will crash)
- // procedure EndOfStreamTimer(
- // uID: UINT; // Timer identifier
- // uMsg: UINT; // Not currently used
- // dwUser: DWord; // User information
- // dw1: DWord; // Windows reserved
- // dw2: DWord // Is also reserved
- // ); stdcall;
- // Milenko end
- public
- {$IFDEF PERF}
- // Just before we started drawing
- // Set in OnRenderStart, Used in OnRenderEnd
- FRenderStart: TReferenceTime;
- // MSR_id for frame time stamp
- FBaseStamp: Integer;
- // MSR_id for true wait time
- FBaseRenderTime: Integer;
- // MSR_id for time frame is late (int)
- FBaseAccuracy: Integer;
- {$ENDIF}
- constructor Create(
- // CLSID for this renderer
- RendererClass: TGUID;
- // Debug ONLY description
- Name: PChar;
- // Aggregated owner object
- Unk: IUnknown;
- // General OLE return code
- hr: HResult);
- destructor Destroy; override;
- // milenko start (added as a workaround for the TBCRendererPosPAssThru/FPosition and Renderer destructor)
- function JoinFilterGraph(pGraph: IFilterGraph; pName: PWideChar): HRESULT; override;
- // milenko end
- // Overriden to say what interfaces we support and where
- function GetMediaPositionInterface(IID: TGUID; out Obj): HResult;
- virtual;
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult;
- override; stdcall;
- function SourceThreadCanWait(CanWait: Boolean): HResult; virtual;
- {$IFDEF DEBUG}
- // Debug only dump of the renderer state
- procedure DisplayRendererState;
- {$ENDIF}
- function WaitForRenderTime: HResult; virtual;
- function CompleteStateChange(OldState: TFilterState): HResult; virtual;
- // Return internal information about this filter
- property IsEndOfStream: Boolean read FIsEOS;
- property IsEndOfStreamDelivered: Boolean read FIsEOSDelivered;
- property IsStreaming: Boolean read FIsStreaming;
- procedure SetAbortSignal(Abort_: Boolean);
- procedure OnReceiveFirstSample(MediaSample: IMediaSample); virtual;
- property RenderEvent: TBCAMEvent read FRenderEvent;
- // Permit access to the transition state
- procedure Ready;
- procedure NotReady;
- function CheckReady: Boolean;
- function GetPinCount: Integer; override;
- function GetPin(n: integer): TBCBasePin; override;
- function GetRealState: TFilterState;
- procedure SendRepaint;
- procedure SendNotifyWindow(Pin: IPin; Handle: HWND);
- function OnDisplayChange: Boolean;
- procedure SetRepaintStatus(Repaint: Boolean);
- // Override the filter and pin interface functions
- function Stop: HResult; override; stdcall;
- function Pause: HResult; override; stdcall;
- function Run(StartTime: TReferenceTime): HResult; override; stdcall;
- function GetState(MSecs: DWord; out State: TFilterState): HResult;
- override; stdcall;
- function FindPin(id: PWideChar; out Pin: IPin): HResult;
- override; stdcall;
- // These are available for a quality management implementation
- procedure OnRenderStart(MediaSample: IMediaSample); virtual;
- procedure OnRenderEnd(MediaSample: IMediaSample); virtual;
- function OnStartStreaming: HResult; virtual;
- function OnStopStreaming: HResult; virtual;
- procedure OnWaitStart; virtual;
- procedure OnWaitEnd; virtual;
- procedure PrepareRender; virtual;
- // Quality management implementation for scheduling rendering
- function ScheduleSample(MediaSample: IMediaSample): Boolean; virtual;
- function GetSampleTimes(MediaSample: IMediaSample;
- out StartTime: TReferenceTime; out EndTime: TReferenceTime): HResult;
- virtual;
- function ShouldDrawSampleNow(MediaSample: IMediaSample;
- StartTime: TReferenceTime; out EndTime: TReferenceTime): HResult; virtual;
- // Lots of end of stream complexities
- procedure TimerCallback;
- procedure ResetEndOfStreamTimer;
- function NotifyEndOfStream: HResult;
- function SendEndOfStream: HResult; virtual;
- function ResetEndOfStream: HResult; virtual;
- function EndOfStream: HResult; virtual;
- // Rendering is based around the clock
- procedure SignalTimerFired;
- function CancelNotification: HResult; virtual;
- function ClearPendingSample: HResult; virtual;
- // Called when the filter changes state
- function Active: HResult; virtual;
- function Inactive: HResult; virtual;
- function StartStreaming: HResult; virtual;
- function StopStreaming: HResult; virtual;
- function BeginFlush: HResult; virtual;
- function EndFlush: HResult; virtual;
- // Deal with connections and type changes
- function BreakConnect: HResult; virtual;
- function SetMediaType(MediaType: PAMMediaType): HResult; virtual;
- function CompleteConnect(ReceivePin: IPin): HResult; virtual;
- // These look after the handling of data samples
- function PrepareReceive(MediaSample: IMediaSample): HResult; virtual;
- function Receive(MediaSample: IMediaSample): HResult; virtual;
- function HaveCurrentSample: Boolean; virtual;
- function GetCurrentSample: IMediaSample; virtual;
- function Render(MediaSample: IMediaSample): HResult; virtual;
- // Derived classes MUST override these
- function DoRenderSample(MediaSample: IMediaSample): HResult;
- virtual; abstract;
- function CheckMediaType(MediaType: PAMMediaType): HResult;
- virtual; abstract;
- // Helper
- procedure WaitForReceiveToComplete;
- (*
- // callback
- property EndOfStreamTimerCB: TFNTimeCallBack read FEndOfStreamTimerCB
- write FEndOfStreamTimerCB;
- *)
- end;
- const
- AVGPERIOD = 4;
- type
- // CBaseVideoRenderer is a renderer class (see its ancestor class) and
- // it handles scheduling of media samples so that they are drawn at the
- // correct time by the reference clock. It implements a degradation
- // strategy. Possible degradation modes are:
- // Drop frames here (only useful if the drawing takes significant time)
- // Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.
- // Signal supplier to change the frame rate - i.e. ongoing skipping.
- // Or any combination of the above.
- // In order to determine what's useful to try we need to know what's going
- // on. This is done by timing various operations (including the supplier).
- // This timing is done by using timeGetTime as it is accurate enough and
- // usually cheaper than calling the reference clock. It also tells the
- // truth if there is an audio break and the reference clock stops.
- // We provide a number of public entry points (named OnXxxStart, OnXxxEnd)
- // which the rest of the renderer calls at significant moments. These do
- // the timing.
- // the number of frames that the sliding averages are averaged over.
- // the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD
- // #define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)
- // Spot the bug in this macro - I can't. but it doesn't work!
- TBCBaseVideoRenderer = class(
- // Base renderer class
- TBCBaseRenderer,
- // Property page guff
- IQualProp,
- // Allow throttling
- IQualityControl)
- protected
- //******************************************************************
- // State variables to control synchronisation
- //******************************************************************
- // Control of sending Quality messages. We need to know whether
- // we are in trouble (e.g. frames being dropped) and where the time
- // is being spent.
- // When we drop a frame we play the next one early.
- // The frame after that is likely to wait before drawing and counting this
- // wait as spare time is unfair, so we count it as a zero wait.
- // We therefore need to know whether we are playing frames early or not.
- // The number of consecutive frames drawn at their normal time (not early)
- // -1 means we just dropped a frame.
- FNormal: Integer;
- {$IFDEF PERF}
- // Don't drop any frames (debug and I'm not keen on people using it!)
- FDrawLateFrames: Bool;
- {$ENDIF}
- // The response to Quality messages says our supplier is handling things.
- // We will allow things to go extra late before dropping frames.
- // We will play very early after he has dropped one.
- FSupplierHandlingQuality: Boolean;
- // Control of scheduling, frame dropping etc.
- // We need to know where the time is being spent so as to tell whether
- // we should be taking action here, signalling supplier or what.
- // The variables are initialised to a mode of NOT dropping frames.
- // They will tell the truth after a few frames.
- // We typically record a start time for an event, later we get the time
- // again and subtract to get the elapsed time, and we average this over
- // a few frames. The average is used to tell what mode we are in.
- // Although these are reference times (64 bit) they are all DIFFERENCES
- // between times which are small. An int will go up to 214 secs before
- // overflow. Avoiding 64 bit multiplications and divisions seems
- // worth while.
- // Audio-video throttling. If the user has turned up audio quality
- // very high (in principle it could be any other stream, not just audio)
- // then we can receive cries for help via the graph manager. In this case
- // we put in a wait for some time after rendering each frame.
- FThrottle: Integer;
- // The time taken to render (i.e. BitBlt) frames controls which component
- // needs to degrade. If the blt is expensive, the renderer degrades.
- // If the blt is cheap it's done anyway and the supplier degrades.
- // Time frames are taking to blt
- FRenderAvg: Integer;
- // Time for last frame blt
- FRenderLast: Integer;
- // Just before we started drawing (mSec) derived from timeGetTime.
- FRenderStart: Integer;
- // When frames are dropped we will play the next frame as early as we can.
- // If it was a false alarm and the machine is fast we slide gently back to
- // normal timing. To do this, we record the offset showing just how early
- // we really are. This will normally be negative meaning early or zero.
- FEarliness: Integer;
- // Target provides slow long-term feedback to try to reduce the
- // average sync offset to zero. Whenever a frame is actually rendered
- // early we add a msec or two, whenever late we take off a few.
- // We add or take off 1/32 of the error time.
- // Eventually we should be hovering around zero. For a really bad case
- // where we were (say) 300mSec off, it might take 100 odd frames to
- // settle down. The rate of change of this is intended to be slower
- // than any other mechanism in Quartz, thereby avoiding hunting.
- FTarget: Integer;
- // The proportion of time spent waiting for the right moment to blt
- // controls whether we bother to drop a frame or whether we reckon that
- // we're doing well enough that we can stand a one-frame glitch.
- // Average of last few wait times (actually we just average how early we were).
- // Negative here means LATE.
- FWaitAvg: Integer;
- // The average inter-frame time.
- // This is used to calculate the proportion of the time used by the
- // three operations (supplying us, waiting, rendering)
- // Average inter-frame time
- FFrameAvg: Integer;
- // duration of last frame.
- FDuration: Integer;
- {$IFDEF PERF}
- // Performance logging identifiers
- // MSR_id for frame time stamp
- FTimeStamp: Integer;
- // MSR_id for true wait time
- FWaitReal: Integer;
- // MSR_id for wait time recorded
- FWait: Integer;
- // MSR_id for time frame is late (int)
- FFrameAccuracy: Integer;
- // MSR_id for lateness at scheduler
- FSchLateTime: Integer;
- // MSR_id for Quality rate requested
- FQualityRate: Integer;
- // MSR_id for Quality time requested
- FQualityTime: Integer;
- // MSR_id for decision code
- FDecision: Integer;
- // MSR_id for trace style debugging
- FDebug: Integer;
- // MSR_id for timing the notifications per se
- FSendQuality: Integer;
- {$ENDIF}
- // original time stamp of frame with no earliness fudges etc.
- FRememberStampforPerf: TReferenceTime;
- {$IFDEF PERF}
- // time when previous frame rendered
- FRememberFrameForPerf: TReferenceTime;
- {$ENDIF}
- // PROPERTY PAGE
- // This has edit fields that show the user what's happening
- // These member variables hold these counts.
- // cumulative frames dropped IN THE RENDERER
- FFramesDropped: Integer;
- // Frames since streaming started seen BY THE RENDERER
- // (some may be dropped upstream)
- FFramesDrawn: Integer;
- // Next two support average sync offset and standard deviation of sync offset.
- // Sum of accuracies in mSec
- FTotAcc: Int64;
- // Sum of squares of (accuracies in mSec)
- FSumSqAcc: Int64;
- // Next two allow jitter calculation. Jitter is std deviation of frame time.
- // Time of prev frame (for inter-frame times)
- FLastDraw: TReferenceTime;
- // Sum of squares of (inter-frame time in mSec)
- FSumSqFrameTime: Int64;
- // Sum of inter-frame times in mSec
- FSumFrameTime: Int64;
- // To get performance statistics on frame rate, jitter etc, we need
- // to record the lateness and inter-frame time. What we actually need are the
- // data above (sum, sum of squares and number of entries for each) but the data
- // is generated just ahead of time and only later do we discover whether the
- // frame was actually drawn or not. So we have to hang on to the data
- // hold onto frame lateness
- FLate: Integer;
- // hold onto inter-frame time
- FFrame: Integer;
- // if streaming then time streaming started
- // else time of last streaming session
- // used for property page statistics
- FStreamingStart: Integer;
- {$IFDEF PERF}
- // timeGetTime*10000+m_llTimeOffset==ref time
- FTimeOffset: Int64;
- {$ENDIF}
- public
- constructor Create(
- // CLSID for this renderer
- RenderClass: TGUID;
- // Debug ONLY description
- Name: PChar;
- // Aggregated owner object
- Unk: IUnknown;
- // General OLE return code
- hr: HResult);
- destructor Destroy; override;
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult;
- override; stdcall;
- // IQualityControl methods - Notify allows audio-video throttling
- function SetSink(QualityControl: IQualityControl): HResult; stdcall;
- function Notify(Filter: IBaseFilter; q: TQuality): HResult; stdcall;
- // These provide a full video quality management implementation
- procedure OnRenderStart(MediaSample: IMediaSample); override;
- procedure OnRenderEnd(MediaSample: IMediaSample); override;
- procedure OnWaitStart; reintroduce;
- procedure OnWaitEnd; reintroduce;
- function OnStartStreaming: HResult; reintroduce;
- function OnStopStreaming: HResult; reintroduce;
- procedure ThrottleWait;
- // Handle the statistics gathering for our quality management
- procedure PreparePerformanceData(Late, Frame: Integer);
- procedure RecordFrameLateness(Late, Frame: Integer); virtual;
- procedure OnDirectRender(MediaSample: IMediaSample); virtual;
- function ResetStreamingTimes: HResult; virtual;
- function ScheduleSample(MediaSample: IMediaSample): Boolean; override;
- function ShouldDrawSampleNow(MediaSample: IMediaSample;
- StartTime: TReferenceTime; out EndTime: TReferenceTime): HResult;
- override;
- function SendQuality(Late, RealStream: TReferenceTime): HResult; virtual;
- // milenko start (TBCBaseFilter made virtual, so just add override here)
- function JoinFilterGraph(Graph: IFilterGraph; Name: PWideChar): HResult; override;
- // milenko end
- //
- // Do estimates for standard deviations for per-frame
- // statistics
- //
- // *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /
- // (m_cFramesDrawn - 2)
- // or 0 if m_cFramesDrawn <= 3
- //
- function GetStdDev(Samples: Integer; out Res: Integer;
- SumSq, Tot: Int64): HResult;
- // IQualProp property page support
- // ??? out <- var function get_FramesDroppedInRenderer(out pcFrames : Integer) : HResult; stdcall;
- function get_FramesDroppedInRenderer(var FramesDropped: Integer): HResult;
- stdcall;
- function get_FramesDrawn(out FramesDrawn: Integer): HResult; stdcall;
- function get_AvgFrameRate(out AvgFrameRate: Integer): HResult; stdcall;
- function get_Jitter(out Jitter: Integer): HResult; stdcall;
- function get_AvgSyncOffset(out Avg: Integer): HResult; stdcall;
- function get_DevSyncOffset(out Dev: Integer): HResult; stdcall;
- end;
-
// milenko start (added TBCPullPin)
-
//
-
// CPullPin
- //
- // object supporting pulling data from an IAsyncReader interface.
- // Given a start/stop position, calls a pure Receive method with each
- // IMediaSample received.
- //
- // This is essentially for use in a MemInputPin when it finds itself
- // connected to an IAsyncReader pin instead of a pushing pin.
- //
- TThreadMsg = (
- TM_Pause, // stop pulling and wait for next message
- TM_Start, // start pulling
- TM_Exit // stop and exit
- );
- TBCPullPin = class(TBCAMThread)
- private
- FReader: IAsyncReader;
- FStart: TReferenceTime;
- FStop: TReferenceTime;
- FDuration: TReferenceTime;
- FSync: Boolean;
- FState: TThreadMsg;
- // running pull method (check m_bSync)
- procedure Process;
- // clean up any cancelled i/o after a flush
- procedure CleanupCancelled;
- // suspend thread from pulling, eg during seek
- function PauseThread: HRESULT;
- // start thread pulling - create thread if necy
- function StartThread: HRESULT;
- // stop and close thread
- function StopThread: HRESULT;
- // called from ProcessAsync to queue and collect requests
- function QueueSample(var tCurrent: TReferenceTime; tAlignStop: TReferenceTime; bDiscontinuity: Boolean): HRESULT;
- function CollectAndDeliver(tStart,tStop: TReferenceTime): HRESULT;
- function DeliverSample(pSample: IMediaSample; tStart,tStop: TReferenceTime): HRESULT;
- protected
- FAlloc: IMemAllocator;
- // override pure thread proc from CAMThread
- function ThreadProc: DWord; override;
- public
- constructor Create;
- destructor Destroy; override;
- // returns S_OK if successfully connected to an IAsyncReader interface
- // from this object
- // Optional allocator should be proposed as a preferred allocator if
- // necessary
- // bSync is TRUE if we are to use sync reads instead of the
- // async methods.
- function Connect(pUnk: IUnknown; pAlloc: IMemAllocator; bSync: Boolean): HRESULT;
- // disconnect any connection made in Connect
- function Disconnect: HRESULT;
- // agree an allocator using RequestAllocator - optional
- // props param specifies your requirements (non-zero fields).
- // returns an error code if fail to match requirements.
- // optional IMemAllocator interface is offered as a preferred allocator
- // but no error occurs if it can't be met.
- function DecideAllocator(pAlloc: IMemAllocator; pProps: PAllocatorProperties): HRESULT;
- // set start and stop position. if active, will start immediately at
- // the new position. Default is 0 to duration
- function Seek(tStart, tStop: TReferenceTime): HRESULT;
- // return the total duration
- function Duration(out ptDuration: TReferenceTime): HRESULT;
- // start pulling data
- function Active: HRESULT;
- // stop pulling data
- function Inactive: HRESULT;
- // helper functions
- function AlignDown(ll: Int64; lAlign: LongInt): Int64;
- function AlignUp(ll: Int64; lAlign: LongInt): Int64;
- // GetReader returns the (addrefed) IAsyncReader interface
- // for SyncRead etc
- function GetReader: IAsyncReader;
- // -- pure --
- // override this to handle data arrival
- // return value other than S_OK will stop data
- function Receive(Sample: IMediaSample): HRESULT; virtual; abstract;
- // override this to handle end-of-stream
- function EndOfStream: HRESULT; virtual; abstract;
- // called on runtime errors that will have caused pulling
- // to stop
- // these errors are all returned from the upstream filter, who
- // will have already reported any errors to the filtergraph.
- procedure OnError(hr: HRESULT); virtual; abstract;
- // flush this pin and all downstream
- function BeginFlush: HRESULT; virtual; abstract;
- function EndFlush: HRESULT; virtual; abstract;
- end;
- // milenko end
- // milenko start (needed to access functions outside. usefull for Filter Development)
- function CreateMemoryAllocator(out Allocator: IMemAllocator): HRESULT;
- function AMGetWideString(Source: WideString; out Dest: PWideChar): HRESULT;
- function CreatePosPassThru(Agg: IUnknown; Renderer: boolean; Pin: IPin; out PassThru: IUnknown): HRESULT; stdcall;
- // milenko end
- // milenko start reftime implementation
- //------------------------------------------------------------------------------
- // File: RefTime.h
- //
- // Desc: DirectShow base classes - defines CRefTime, a class that manages
- // reference times.
- //
- // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------------------------
- //
- // CRefTime
- //
- // Manage reference times.
- // Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)
- // functions providing simple comparison, conversion and arithmetic.
- //
- // A reference time (at the moment) is a unit of seconds represented in
- // 100ns units as is used in the Win32 FILETIME structure. BUT the time
- // a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it
- // will either be stream time or reference time depending upon context
- //
- // This class provides simple arithmetic operations on reference times
- //
- // keep non-virtual otherwise the data layout will not be the same as
- // REFERENCE_TIME
- // -----
- // note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but
- // you will need to do so explicitly
- // -----
- type
- TBCRefTime = object
- public
- // *MUST* be the only data member so that this class is exactly
- // equivalent to a REFERENCE_TIME.
- // Also, must be *no virtual functions*
- FTime: TReferenceTime;
- // DCODER: using Create_ as contructor replacement ...
- procedure Create_; overload;
- procedure Create_(msecs: Longint); overload;
- // delphi 5 doesn't like "const rt: TBCRefTime" ???
- function SetTime(var rt: TBCRefTime): TBCRefTime; overload;
- function SetTime(var ll: LONGLONG): TBCRefTime; overload;
- function AddTime(var rt: TBCRefTime): TBCRefTime; overload;
- function SubstractTime(var rt: TBCRefTime): TBCRefTime; overload;
- function Millisecs: Longint;
- function GetUnits: LONGLONG;
- end;
- // milenko end;
- // milenko start schedule implementation
- //------------------------------------------------------------------------------
- // File: Schedule.cpp
- //
- // Desc: DirectShow base classes.
- //
- // Copyright (c) 1996-2002 Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------------------------
- type
- TBCAdvisePacket = class
- public
- FNext : TBCAdvisePacket;
- FAdviseCookie: DWORD;
- FEventTime : TReferenceTime; // Time at which event should be set
- FPeriod : TReferenceTime; // Periodic time
- FNotify : THandle; // Handle to event or semephore
- FPeriodic : Boolean; // TRUE => Periodic event
- constructor Create; overload;
- constructor Create(Next: TBCAdvisePacket; Time: LONGLONG); overload;
- procedure InsertAfter(Packet: TBCAdvisePacket);
- // That is, is it the node that represents the end of the list
- function IsZ: Boolean;
- function RemoveNext: TBCAdvisePacket;
- procedure DeleteNext;
- function Next: TBCAdvisePacket;
- function Cookie: DWORD;
- end;
- TBCAMSchedule = class(TBCBaseObject)
- private
- // Structure is:
- // head -> elmt1 -> elmt2 -> z -> null
- // So an empty list is: head -> z -> null
- // Having head & z as links makes insertaion,
- // deletion and shunting much easier.
- FHead,
- FZ : TBCAdvisePacket; // z is both a tail and a sentry
- FNextCookie : DWORD; // Strictly increasing
- FAdviseCount: DWORD; // Number of elements on list
- FSerialize : TBCCritSec;
- // Event that we should set if the packed added above will be the next to fire.
- FEvent : THandle;
- // Rather than delete advise packets, we cache them for future use
- FAdviseCache: TBCAdvisePacket;
- FCacheCount : DWORD;
- // AddAdvisePacket: adds the packet, returns the cookie (0 if failed)
- function AddAdvisePacket(Packet: TBCAdvisePacket): DWORD; overload;
- // A Shunt is where we have changed the first element in the
- // list and want it re-evaluating (i.e. repositioned) in
- // the list.
- procedure ShuntHead;
- procedure Delete(Packet: TBCAdvisePacket);// This "Delete" will cache the Link
- public
- // ev is the event we should fire if the advise time needs re-evaluating
- constructor Create(Event: THandle);
- destructor Destroy; override;
- function GetAdviseCount: DWORD;
- function GetNextAdviseTime: TReferenceTime;
- // We need a method for derived classes to add advise packets, we return the cookie
- function AddAdvisePacket(const Time1, Time2: TReferenceTime; h: THandle;
- Periodic: Boolean): DWORD; overload;
- // And a way to cancel
- function Unadvise(AdviseCookie: DWORD): HRESULT;
- // Tell us the time please, and we'll dispatch the expired events.
- // We return the time of the next event.
- // NB: The time returned will be "useless" if you start adding extra Advises.
- // But that's the problem of
- // whoever is using this helper class (typically a clock).
- function Advise(const Time_: TReferenceTime): TReferenceTime;
- // Get the event handle which will be set if advise time requires re-evaluation.
- function GetEvent: THandle;
- procedure DumpLinkedList;
- end;
- // milenko end
- // milenko start refclock implementation
- //------------------------------------------------------------------------------
- // File: RefClock.h
- //
- // Desc: DirectShow base classes - defines the IReferenceClock interface.
- //
- // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------------------------
- (* This class hierarchy will support an IReferenceClock interface so
- that an audio card (or other externally driven clock) can update the
- system wide clock that everyone uses.
- The interface will be pretty thin with probably just one update method
- This interface has not yet been defined.
- *)
- (* This abstract base class implements the IReferenceClock
- * interface. Classes that actually provide clock signals (from
- * whatever source) have to be derived from this class.
- *
- * The abstract class provides implementations for:
- * CUnknown support
- * locking support (CCritSec)
- * client advise code (creates a thread)
- *
- * Question: what can we do about quality? Change the timer
- * resolution to lower the system load? Up the priority of the
- * timer thread to force more responsive signals?
- *
- * During class construction we create a worker thread that is destroyed during
- * destuction. This thread executes a series of WaitForSingleObject calls,
- * waking up when a command is given to the thread or the next wake up point
- * is reached. The wakeup points are determined by clients making Advise
- * calls.
- *
- * Each advise call defines a point in time when they wish to be notified. A
- * periodic advise is a series of these such events. We maintain a list of
- * advise links and calculate when the nearest event notification is due for.
- * We then call WaitForSingleObject with a timeout equal to this time. The
- * handle we wait on is used by the class to signal that something has changed
- * and that we must reschedule the next event. This typically happens when
- * someone comes in and asks for an advise link while we are waiting for an
- * event to timeout.
- *
- * While we are modifying the list of advise requests we
- * are protected from interference through a critical section. Clients are NOT
- * advised through callbacks. One shot clients have an event set, while
- * periodic clients have a semaphore released for each event notification. A
- * semaphore allows a client to be kept up to date with the number of events
- * actually triggered and be assured that they can't miss multiple events being
- * set.
- *
- * Keeping track of advises is taken care of by the CAMSchedule class.
- *)
- type
- TBCBaseReferenceClock = class(TBCUnknown, IReferenceClock)
- private
- FLock : TBCCritSec;
- FAbort : Boolean; // Flag used for thread shutdown
- FThread : THandle; // Thread handle
- FPrivateTime : TReferenceTime; // Current best estimate of time
- FPrevSystemTime : DWORD; // Last vaule we got from timeGetTime
- FLastGotTime : TReferenceTime; // Last time returned by GetTime
- FNextAdvise : TReferenceTime; // Time of next advise
- FTimerResolution: DWORD;
- {$IFDEF PERF}
- FGetSystemTime : integer;
- {$ENDIF}
- function AdviseThread: HRESULT; // Method in which the advise thread runs
- protected
- FSchedule : TBCAMSchedule;
- public
- constructor Create(Name: String; Unk: IUnknown; out hr: HRESULT; Sched:
- TBCAMSchedule = nil);
- destructor Destroy; override; // Don't let me be created on the stack!
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult; override; stdcall;
- // IReferenceClock methods
- // Derived classes must implement GetPrivateTime(). All our GetTime
- // does is call GetPrivateTime and then check so that time does not
- // go backwards. A return code of S_FALSE implies that the internal
- // clock has gone backwards and GetTime time has halted until internal
- // time has caught up. (Don't know if this will be much use to folk,
- // but it seems odd not to use the return code for something useful.)
- function GetTime(out Time: int64): HResult; stdcall;
- // When this is called, it sets m_rtLastGotTime to the time it returns.
- // Provide standard mechanisms for scheduling events
- // Ask for an async notification that a time has elapsed */
- function AdviseTime(
- BaseTime, // base reference time
- StreamTime: int64; // stream offset time
- Event: THandle; // advise via this event
- out AdviseCookie: DWORD // where your cookie goes
- ): HResult; stdcall;
- // Ask for an asynchronous periodic notification that a time has elapsed
- function AdvisePeriodic(
- const StartTime, // starting at this time
- PeriodTime: int64; // time between notifications
- Semaphore: THandle; // advise via a semaphore
- out AdviseCookie: DWORD // where your cookie goes
- ): HResult; stdcall;
- (* Cancel a request for notification(s) - if the notification was
- * a one shot timer then this function doesn't need to be called
- * as the advise is automatically cancelled, however it does no
- * harm to explicitly cancel a one-shot advise. It is REQUIRED that
- * clients call Unadvise to clear a Periodic advise setting.
- *)
- function Unadvise(AdviseCookie: DWORD): HResult; stdcall;
- // Methods for the benefit of derived classes or outer objects
- // GetPrivateTime() is the REAL clock. GetTime is just a cover for
- // it. Derived classes will probably override this method but not
- // GetTime() itself.
- // The important point about GetPrivateTime() is it's allowed to go
- // backwards. Our GetTime() will keep returning the LastGotTime
- // until GetPrivateTime() catches up.
- function GetPrivateTime: TReferenceTime; virtual;
- // Provide a method for correcting drift
- function SetTimeDelta(const TimeDelta: TReferenceTime): HRESULT; stdcall;
- function GetSchedule: TBCAMSchedule;
- // Thread stuff
- // Wakes thread up. Need to do this if time to next advise needs reevaluating.
- procedure TriggerThread;
- end;
- // milenko end
- // milenko start sysclock implementation
- //------------------------------------------------------------------------------
- // File: SysClock.h
- //
- // Desc: DirectShow base classes - defines a system clock implementation of
- // IReferenceClock.
- //
- // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------------------------
- const
- IID_IPersist : TGUID = '{0000010C-0000-0000-C000-000000000046}';
- type
- TBCSystemClock = class(TBCBaseReferenceClock, IAMClockAdjust, IPersist)
- public
- constructor Create(Name: WideString; Unk : IUnknown; out hr : HRESULT);
- function NonDelegatingQueryInterface(const IID: TGUID; out Obj): HResult; override; stdcall;
- // Yield up our class id so that we can be persisted
- // Implement required Ipersist method
- function GetClassID(out classID: TCLSID): HResult; stdcall;
- // IAMClockAdjust methods
- function SetClockDelta(rtDelta: TReferenceTime): HResult; stdcall;
- end;
- {$IFDEF DEBUG}
- procedure DbgLog(obj: TBCBaseObJect; const msg: string); overload;
- procedure DbgLog(const msg: string); overload;
- procedure DbgAssert(const Message, Filename: string; LineNumber: Integer;
- ErrorAddr: Pointer);
- {$ENDIF}
- function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult; stdcall;
- function DllCanUnloadNow: HResult; stdcall;
- function DllRegisterServer: HResult; stdcall;
- function DllUnregisterServer: HResult; stdcall;
- (* milenko start (needed for TBCBaseReferenceClock and TBCVideoTransformFilter ) *)
- {$IFDEF PERF}
- procedure MSR_START(Id_: Integer);
- procedure MSR_STOP(Id_: Integer);
- procedure MSR_INTEGER(Id_, i: Integer);
- function MSR_REGISTER(s: String): Integer;
- {$ENDIF}
- (* milenko end *)
- implementation
- var
- ObjectCount : Integer;
- FactoryCount : Integer;
- TemplatesVar : TBCFilterTemplate;
- // milenko start (added global variables instead of local constants)
- IsCheckedVersion: Bool = False;
- IsTimeKillSynchronousFlagAvailable: Bool = False;
- MsgId: Cardinal = 0;
- // milenko end
- {$IFDEF DEBUG}
- {$IFNDEF MESSAGE}
- DebugFile : TextFile;
- {$ENDIF}
- procedure DbgLog(obj: TBCBaseObJect; const msg: string);
- begin
- {$IFNDEF MESSAGE}
- if (obj = nil) then
- Writeln(DebugFile, TimeToStr(time) +' > '+ msg) else
- Writeln(DebugFile, TimeToStr(time) +' > '+ format('Object: %s, msg: %s.',[obj.FName, msg]));
- Flush(DebugFile);
- {$ELSE}
- if (obj = nil) then OutputDebugString(PChar(TimeToStr(time) +' > '+ msg)) else
- OutputDebugString(PChar(TimeToStr(time) +' > '+ format('Object: %s, msg: %s.',[obj.FName, msg])));
- {$ENDIF}
- end;
- procedure DbgLog(const msg: string); overload;
- begin
- {$IFNDEF MESSAGE}
- Writeln(DebugFile, TimeToStr(time) +' > '+ msg);
- Flush(DebugFile);
- {$ELSE}
- OutputDebugString(PChar(TimeToStr(time) +' > '+ msg));
- {$ENDIF}
- end;
- procedure DbgAssert(const Message, Filename: string; LineNumber: Integer;
- ErrorAddr: Pointer);
- begin
- DbgLog(format('[ASSERT] %s (%s) line: %d, adr: $%x',
- [Message, Filename, LineNumber, Integer(ErrorAddr)]));
- end;
- {$ENDIF}
- // -----------------------------------------------------------------------------
- // TBCMediaType
- // -----------------------------------------------------------------------------
- function TBCMediaType.Equal(mt: TBCMediaType): boolean;
- begin
- result := ((IsEqualGUID(Mediatype.majortype,mt.MediaType.majortype) = True) and
- (IsEqualGUID(Mediatype.subtype,mt.MediaType.subtype) = True) and
- (IsEqualGUID(Mediatype.formattype,mt.MediaType.formattype) = True) and
- (Mediatype.cbFormat = mt.MediaType.cbFormat) and
- ( (Mediatype.cbFormat = 0) or
- (CompareMem(Mediatype.pbFormat, mt.MediaType.pbFormat, Mediatype.cbFormat))));
- end;
- function TBCMediaType.Equal(mt: PAMMediaType): boolean;
- begin
- result := ((IsEqualGUID(Mediatype.majortype,mt.majortype) = True) and
- (IsEqualGUID(Mediatype.subtype,mt.subtype) = True) and
- (IsEqualGUID(Mediatype.formattype,mt.formattype) = True) and
- (Mediatype.cbFormat = mt.cbFormat) and
- ( (Mediatype.cbFormat = 0) or
- (CompareMem(Mediatype.pbFormat, mt.pbFormat, Mediatype.cbFormat))));
- end;
- function TBCMediaType.MatchesPartial(Partial: PAMMediaType): boolean;
- begin
- result := false;
- if (not IsEqualGUID(partial.majortype, GUID_NULL) and
- not IsEqualGUID(MediaType.majortype, partial.majortype)) then exit;
- if (not IsEqualGUID(partial.subtype, GUID_NULL) and
- not IsEqualGUID(MediaType.subtype, partial.subtype)) then exit;
- if not IsEqualGUID(partial.formattype, GUID_NULL) then
- begin
- if not IsEqualGUID(MediaType.formattype, partial.formattype) then exit;
- if (MediaType.cbFormat <> partial.cbFormat) then exit;
- if ((MediaType.cbFormat <> 0) and
- (CompareMem(MediaType.pbFormat, partial.pbFormat, MediaType.cbFormat) <> false)) then exit;
- end;
- result := True;
- end;
- function TBCMediaType.IsPartiallySpecified: boolean;
- begin
- if (IsEqualGUID(Mediatype.majortype, GUID_NULL) or
- IsEqualGUID(Mediatype.formattype, GUID_NULL)) then result := True
- else result := false;
- end;
- function TBCMediaType.IsValid: boolean;
- begin
- result := not IsEqualGUID(MediaType.majortype,GUID_NULL);
- end;
- procedure TBCMediaType.InitMediaType;
- begin
- ZeroMemory(MediaType, sizeof(TAMMediaType));
- MediaType.lSampleSize := 1;
- MediaType.bFixedSizeSamples := True;
- end;
- function TBCMediaType.FormatLength: Cardinal;
- begin
- result := MediaType.cbFormat
- end;
- // -----------------------------------------------------------------------------
- // milenko start
- function AMGetWideString(Source: WideString; out Dest: PWideChar): HRESULT;
- var NameLen: Cardinal;
- begin
- if not assigned(@Dest) then
- begin
- Result := E_POINTER;
- Exit;
- end;
- nameLen := sizeof(WCHAR) * (length(source)+1);
- Dest := CoTaskMemAlloc(nameLen);
- if (Dest = nil) then
- begin
- Result := E_OUTOFMEMORY;
- Exit;
- end;
- CopyMemory(Dest, PWideChar(Source), nameLen);
- Result := NOERROR;
- end;
- {
- function AMGetWideString(Source: WideString; out Dest: PWideChar): HRESULT;
- type TWideCharArray = array of WideChar;
- var NameLen: Cardinal;
- begin
- if Source = '' then
- begin
- dest := nil;
- result := S_OK;
- exit;
- end;
- assert(@dest <> nil);
- nameLen := (length(Source)+1)*2;
- Dest := CoTaskMemAlloc(nameLen);
- if(Dest = nil) then
- begin
- result := E_OUTOFMEMORY;
- exit;
- end;
- CopyMemory(dest, pointer(Source), nameLen-1);
- TWideCharArray(dest)[(nameLen div 2)-1] := #0;
- result := NOERROR;
- end;
- }
- // milenko end
- // -----------------------------------------------------------------------------
- function CreateMemoryAllocator(out Allocator: IMemAllocator): HRESULT;
- begin
- result := CoCreateInstance(CLSID_MemoryAllocator, nil, CLSCTX_INPROC_SERVER,
- IID_IMemAllocator, Allocator);
- end;
- // Put this one here rather than in ctlutil.cpp to avoid linking
- // anything brought in by ctlutil.cpp
- function CreatePosPassThru(Agg: IUnknown; Renderer: boolean; Pin: IPin; out PassThru: IUnknown): HRESULT; stdcall;
- var
- UnkSeek: IUnknown;
- APassThru: ISeekingPassThru;
- begin
- PassThru := nil;
- result := CoCreateInstance(CLSID_SeekingPassThru, Agg, CLSCTX_INPROC_SERVER,
- IUnknown, UnkSeek);
- if FAILED(result) then exit;
- result := UnkSeek.QueryInterface(IID_ISeekingPassThru, APassThru);
- if FAILED(result) then
- begin
- UnkSeek := nil;
- exit;
- end;
- result := APassThru.Init(Renderer, Pin);
- APassThru := nil;
- if FAILED(result) then
- begin
- UnkSeek := nil;
- exit;
- end;
- PassThru := UnkSeek;
- result := S_OK;
- end;
- // -----------------------------------------------------------------------------
- function Templates: TBCFilterTemplate;
- begin
- if TemplatesVar = nil then TemplatesVar := TBCFilterTemplate.Create;
- result := TemplatesVar;
- end;
- function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult; stdcall;
- var
- Factory: TBCClassFactory;
- begin
- Factory := Templates.GetFactoryFromClassID(CLSID);
- if Factory <> nil then
- if Factory.GetInterface(IID, Obj) then
- Result := S_OK
- else
- Result := E_NOINTERFACE
- else
- begin
- Pointer(Obj) := nil;
- Result := CLASS_E_CLASSNOTAVAILABLE;
- end;
- end;
- function DllCanUnloadNow: HResult; stdcall;
- begin
- if (ObjectCount = 0) and (FactoryCount = 0) then
- result := S_OK else result := S_FALSE;;
- end;
- function DllRegisterServer: HResult; stdcall;
- begin
- if Templates.RegisterServer(True) then result := S_OK else result := E_FAIL;
- end;
- function DllUnregisterServer: HResult; stdcall;
- begin
- if Templates.RegisterServer(false) then result := S_OK else result := E_FAIL;
- end;
- { TBCClassFactory }
- constructor TBCClassFactory.CreateFilter(ComClass: TBCUnknownClass; Name: string;
- const ClassID: TGUID; const Category: TGUID; Merit: LongWord;
- PinCount: Cardinal; Pins: PRegFilterPins);
- begin
- Templates.AddObjectFactory(Self);
- FComClass := ComClass;
- FName := Name;
- FClassID := ClassID;
- FCategory := Category;
- FMerit := Merit;
- FPinCount := PinCount;
- FPins := Pins;
- end;
- constructor TBCClassFactory.CreatePropertyPage(ComClass: TFormPropertyPageClass; const ClassID: TGUID);
- begin
- Templates.AddObjectFactory(Self);
- FPropClass := ComClass;
- FClassID := ClassID;
- FCategory := ClassID;
- end;
- function TBCClassFactory.CreateInstance(const unkOuter: IUnKnown;
- const iid: TIID; out obj): HResult;
- var
- ComObject: TBCUnknown;
- PropObject: TFormPropertyPage;
- begin
- if @obj = nil then
- begin
- Result := E_POINTER;
- Exit;
- end;
- Pointer(obj) := nil;
- if FPropClass <> nil then
- begin
- PropObject := TFormPropertyPageClass(FPropClass).Create(nil);
- PropObject.FPropertyPage := TBCBasePropertyPage.Create('',nil, PropObject);
- Result := PropObject.QueryInterface(IID, obj);
- end
- else
- begin
- ComObject := TBCUnknownClass(FComClass).CreateFromFactory(self, unkOuter);
- Result := ComObject.QueryInterface(IID, obj);
- if ComObject.FRefCount = 0 then ComObject.Free;
- end;
- end;
- procedure TBCClassFactory.UpdateRegistry(Register: Boolean);
- var
- FileName: array[0..MAX_PATH-1] of Char;
- ClassID, ServerKeyName: String;
- begin
- ClassID := GUIDToString(FClassID);
- ServerKeyName := 'CLSID' + ClassID + '' + 'InprocServer32';
- if Register then
- begin
- CreateRegKey('CLSID' + ClassID, '', FName);
- GetModuleFileName(hinstance, FileName, MAX_PATH);
- CreateRegKey(ServerKeyName, '', FileName);
- CreateRegKey(ServerKeyName, 'ThreadingModel', 'Both');
- end else
- begin
- DeleteRegKey(ServerKeyName);
- DeleteRegKey('CLSID' + ClassID);
- end;
- end;
- function TBCClassFactory.RegisterFilter(FilterMapper: IFilterMapper; Register: Boolean): boolean;
- type
- TDynArrayPins = array of TRegFilterPins;
- TDynArrayPinType = array of TRegPinTypes;
- var
- i, j: integer;
- FilterGUID: TGUID;
- begin
- result := Succeeded(FilterMapper.UnregisterFilter(FClassID));
- if Register then
- begin
- result := Succeeded(FilterMapper.RegisterFilter(FClassID, StringToOleStr(FName), FMerit));
- if result then
- begin
- for i := 0 to FPinCount - 1 do
- begin
- if TDynArrayPins(FPins)[i].oFilter = nil then
- FilterGUID := GUID_NULL else
- FilterGUID := TDynArrayPins(FPins)[i].oFilter^;
- result := Succeeded(FilterMapper.RegisterPin(FClassID,
- TDynArrayPins(FPins)[i].strName,
- TDynArrayPins(FPins)[i].bRendered,
- TDynArrayPins(FPins)[i].bOutput,
- TDynArrayPins(FPins)[i].bZero,
- TDynArrayPins(FPins)[i].bMany,
- FilterGUID,
- TDynArrayPins(FPins)[i].strConnectsToPin));
- if result then
- begin
- for j := 0 to TDynArrayPins(FPins)[i].nMediaTypes - 1 do
- begin
- result := Succeeded(FilterMapper.RegisterPinType(FClassID,
- TDynArrayPins(FPins)[i].strName,
- TDynArrayPinType(TDynArrayPins(FPins)[i].lpMediaType)[j].clsMajorType^,
- TDynArrayPinType(TDynArrayPins(FPins)[i].lpMediaType)[j].clsMinorType^));
- if not result then break;
- end;
- if not result then break;
- end;
- if not result then break;
- end;
- end;
- end;
- end;
- function TBCClassFactory.RegisterFilter(FilterMapper: IFilterMapper2; Register: Boolean): boolean;
- var
- RegFilter: TRegFilter2;
- begin
- result := Succeeded(FilterMapper.UnregisterFilter(FCategory, nil, FClassID));
- // milenko start (bugfix for Windows 98)
- // Windows 98 fails when unregistering a Property Page, so the whole
- // DLLUnregisterServer function fails without unregistering the Filter.
- if not result and not Register and (FName = '') then Result := True;
- // milenko end
- if Register then
- begin
- RegFilter.dwVersion := 1;
- RegFilter.dwMerit := FMerit;
- RegFilter.cPins := FPinCount;
- RegFilter.rgPins := FPins;
- result := Succeeded(FilterMapper.RegisterFilter(FClassID, PWideChar(WideString(FName)),
- nil, @FCategory, nil, RegFilter));
- end;
- end;
- function TBCClassFactory._AddRef: Integer;
- begin
- result := InterlockedIncrement(FactoryCount);
- end;
- function TBCClassFactory._Release: Integer;
- begin
- result := InterlockedDecrement(FactoryCount);
- end;
- function TBCClassFactory.LockServer(fLock: BOOL): HResult;
- begin
- Result := CoLockObjectExternal(Self, fLock, True);
- if flock then InterlockedIncrement(ObjectCount)
- else InterlockedDecrement(ObjectCount);
- end;
- function TBCClassFactory.QueryInterface(const IID: TGUID; out Obj): HResult;
- begin
- if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
- end;
- { TBCFilterTemplate }
- procedure TBCFilterTemplate.AddObjectFactory(Factory: TBCClassFactory);
- begin
- Factory.FNext := FFactoryList;
- FFactoryList := Factory;
- end;
- constructor TBCFilterTemplate.Create;
- begin
- FFactoryList := nil;
- end;
- destructor TBCFilterTemplate.Destroy;
- var AFactory: TBCClassFactory;
- begin
- while FFactoryList <> nil do
- begin
- AFactory := FFactoryList;
- FFactoryList := AFactory.FNext;
- AFactory.Free;
- end;
- inherited Destroy;
- end;
- function TBCFilterTemplate.GetFactoryFromClassID(const CLSID: TGUID): TBCClassFactory;
- var AFactory: TBCClassFactory;
- begin
- result := nil;
- AFactory := FFactoryList;
- while AFactory <> nil do
- begin
- if IsEqualGUID(CLSID, AFactory.FClassID) then
- begin
- result := AFactory;
- break;
- end;
- AFactory := AFactory.FNext;
- end;
- end;
- function TBCFilterTemplate.RegisterServer(Register: Boolean): boolean;
- var
- {$IFDEF DEBUG}
- Filename: array[0..MAX_PATH-1] of Char;
- {$ENDIF}
- FilterMapper : IFilterMapper;
- FilterMapper2: IFilterMapper2;
- Factory: TBCClassFactory;
- begin
- result := false;
- {$IFDEF DEBUG}
- GetModuleFileName(hinstance, Filename, sizeof(Filename));
- DbgLog('TBCFilterTemplate.RegisterServer in ' + Filename);
- {$ENDIF}
- if Failed(CoCreateInstance(CLSID_FilterMapper2, nil, CLSCTX_INPROC_SERVER, IFilterMapper2, FilterMapper2)) then
- if Failed(CoCreateInstance(CLSID_FilterMapper, nil, CLSCTX_INPROC_SERVER, IFilterMapper, FilterMapper)) then exit;
- Factory := FFactoryList;
- while Factory <> nil do
- begin
- Factory.UpdateRegistry(false);
- if FilterMapper2 <> nil then
- result := Factory.RegisterFilter(FilterMapper2, Register)
- else result := Factory.RegisterFilter(FilterMapper, Register);
- if not result then break else Factory.UpdateRegistry(register);
- Factory := Factory.FNext;
- end;
- FilterMapper := nil;
- FilterMapper2 := nil;
- end;
- { TBCBaseObject }
- constructor TBCBaseObject.Create(Name: string);
- begin
- {$IFDEF DEBUG}
- DbgLog('[' + ClassName + ': ' + Name + '] CREATE');
- {$ENDIF}
- FName := name;
- end;
- destructor TBCBaseObject.Destroy;
- begin
- {$IFDEF DEBUG}
- DbgLog('[' + ClassName + ': ' + FName + '] FREE');
- {$ENDIF}
- inherited;
- end;
- procedure TBCBaseObject.FreeInstance;
- begin
- inherited;
- InterlockedDecrement(ObjectCount);
- end;
- class function TBCBaseObject.NewInstance: TObject;
- begin
- result := inherited NewInstance;
- InterlockedIncrement(ObjectCount);
- end;
- class function TBCBaseObject.ObjectsActive: integer;
- begin
- result := ObjectCount;
- end;
- { TBCUnknown }
- function TBCUnknown.QueryInterface(const IID: TGUID; out Obj): HResult;
- begin
- if FOwner <> nil then
- Result := IUnknown(FOwner).QueryInterface(IID, Obj)
- else
- Result := NonDelegatingQueryInterface(IID, Obj);
- end;
- function TBCUnknown._AddRef: Integer;
- begin
- if FOwner <> nil then
- Result := IUnknown(FOwner)._AddRef else
- Result := NonDelegatingAddRef;
- end;
- function TBCUnknown._Release: Integer;
- begin
- if FOwner <> nil then
- Result := IUnknown(FOwner)._Release else
- Result := NonDelegatingRelease;
- end;
- function TBCUnknown.NonDelegatingQueryInterface(const IID: TGUID;
- out Obj): HResult;
- begin
- if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
- end;
- function TBCUnknown.NonDelegatingAddRef: Integer;
- begin
- Result := InterlockedIncrement(FRefCount);
- end;
- function TBCUnknown.NonDelegatingRelease: Integer;
- begin
- Result := InterlockedDecrement(FRefCount);
- if Result = 0 then Destroy;
- end;
- function TBCUnknown.GetOwner: IUnKnown;
- begin
- result := IUnKnown(FOwner);
- end;
- constructor TBCUnknown.Create(name: string; Unk: IUnKnown);
- begin
- inherited Create(name);
- FOwner := Pointer(Unk);
- end;
- constructor TBCUnknown.CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnKnown);
- begin
- Create(Factory.FName, Controller);
- end;
- { TBCBaseFilter }
- constructor TBCBaseFilter.Create(Name: string; Unk: IUnKnown;
- Lock: TBCCritSec; const clsid: TGUID);
- begin
- inherited Create(Name, Unk);
- FLock := Lock;
- Fclsid := clsid;
- FState := State_Stopped;
- FClock := nil;
- FGraph := nil;
- FSink := nil;
- FFilterName := '';
- FPinVersion := 1;
- Assert(FLock <> nil, 'Lock = nil !');
- end;
- constructor TBCBaseFilter.Create(Name: string; Unk: IUnKnown;
- Lock: TBCCritSec; const clsid: TGUID; out hr: HRESULT);
- begin
- Create(Name, Unk, Lock, clsid);
- assert(@hr <> nil, 'Unreferenced parameter: hr');
- end;
- constructor TBCBaseFilter.CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown);
- begin
- Create(Factory.FName,Controller, TBCCritSec.Create, Factory.FClassID);
- end;
- destructor TBCBaseFilter.destroy;
- begin
- FFilterName := '';
- FClock := nil;
- FLock.Free;
- inherited;
- end;
- function TBCBaseFilter.EnumPins(out ppEnum: IEnumPins): HRESULT;
- begin
- // Create a new ref counted enumerator
- ppEnum := TBCEnumPins.Create(self, nil);
- if ppEnum = nil then result := E_OUTOFMEMORY else result := NOERROR;
- end;
- function TBCBaseFilter.FindPin(Id: PWideChar; out Pin: IPin): HRESULT;
- var
- i: integer;
- APin: TBCBasePin;
- begin
- // We're going to search the pin list so maintain integrity
- FLock.Lock;
- try
- for i := 0 to GetPinCount - 1 do
- begin
- APin := GetPin(i);
- ASSERT(APin <> nil);
- if (APin.FPinName = WideString(Id)) then
- begin
- // Found one that matches
- // AddRef() and return it
- Pin := APin;
- result := S_OK;
- exit;
- end;
- end;
- Pin := nil;
- result := VFW_E_NOT_FOUND;
- finally
- FLock.UnLock;
- end;
- end;
- function TBCBaseFilter.GetClassID(out classID: TCLSID): HResult;
- begin
- classID := FCLSID;
- result := NOERROR;
- end;
- function TBCBaseFilter.GetFilterGraph: IFilterGraph;
- begin
- result := FGRaph;
- end;
- function TBCBaseFilter.GetPinVersion: LongInt;
- begin
- result := FPinVersion;
- end;
- function TBCBaseFilter.GetState(dwMilliSecsTimeout: DWORD;
- out State: TFilterState): HRESULT;
- begin
- State := FState;
- result := S_OK;
- end;
- function TBCBaseFilter.GetSyncSource(out pClock: IReferenceClock): HRESULT;
- begin
- FLock.Lock;
- try
- pClock := FClock;
- finally
- result := NOERROR;
- FLock.UnLock;
- end;
- end;
- procedure TBCBaseFilter.IncrementPinVersion;
- begin
- InterlockedIncrement(FPinVersion)
- end;
- function TBCBaseFilter.IsActive: boolean;
- begin
- FLock.Lock;
- try
- result := ((FState = State_Paused) or (FState = State_Running));
- finally
- FLock.UnLock;
- end;
- end;
- function TBCBaseFilter.IsStopped: boolean;
- begin
- result := (FState = State_Stopped);
- end;
- function TBCBaseFilter.JoinFilterGraph(pGraph: IFilterGraph;
- pName: PWideChar): HRESULT;
- begin
- FLock.Lock;
- try
- //Henri: This implementation seem to be stupid but it's the exact conversion ?????
- // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)
- Pointer(FGraph) := Pointer(pGraph);
- if (FGraph <> nil) then
- begin
- if FAILED(FGraph.QueryInterface(IID_IMediaEventSink, FSink)) then
- ASSERT(FSink = nil)
- else FSink._Release; // we do NOT keep a reference on it.
- end
- else
- begin
- // if graph pointer is null, then we should
- // also release the IMediaEventSink on the same object - we don't
- // refcount it, so just set it to null
- Pointer(FSink) := nil;
- end;
- FFilterName := '';
- if assigned(pName) then FFilterName := WideString(pName);
- result := NOERROR;
- finally
- FLock.UnLock;
- end;
- end;
- function TBCBaseFilter.NotifyEvent(EventCode, EventParam1,
- EventParam2: Integer): HRESULT;
- var
- Filter : IBaseFilter;
- begin
- // Snapshot so we don't have to lock up
- if assigned(FSink) then
- begin
- QueryInterface(IID_IBaseFilter,Filter);
- if (EC_COMPLETE = EventCode) then EventParam2 := LongInt(Filter);
- result := FSink.Notify(EventCode, EventParam1, EventParam2);
- Filter := nil;
- end
- else
- result := E_NOTIMPL;
- end;
- function TBCBaseFilter.Pause: HRESULT;
- var
- c: integer;
- pin: TBCBasePin;
- begin
- FLock.Lock;
- try
- if FState = State_Stopped then
- begin
- for c := 0 to GetPinCount - 1 do
- begin
- Pin := GetPin(c);
- // Disconnected pins are not activated - this saves pins
- // worrying about this state themselves
- if Pin.IsConnected then
- begin
- result := Pin.Active;
- if FAILED(result) then exit;
- end;
- end;
- end;
- // notify all pins of the change to active state
- FState := State_Paused;
- result := S_OK;
- finally
- FLock.UnLock;
- end;
- end;
- function TBCBaseFilter.QueryFilterInfo(out pInfo: TFilterInfo): HRESULT;
- var
- len: Integer;
- begin
- len := Length(pInfo.achName)-1;
- if (Length(FFilterName) > 0) then
- if (Length(FFilterName) > len) then
- begin
- CopyMemory(@pInfo.achName, PWideChar(FFilterName), len * SizeOf(WCHAR));
- pInfo.achName[len] := #0;
- end
- else
- CopyMemory(@pInfo.achName, PWideChar(FFilterName), (Length(FFilterName)+1) * SizeOf(WCHAR))
- else
- pInfo.achName[0] := #0;
- pInfo.pGraph := FGraph;
- result := NOERROR;
- end;
- function TBCBaseFilter.QueryVendorInfo(out pVendorInfo: PWideChar): HRESULT;
- begin
- result := E_NOTIMPL;
- end;
- function TBCBaseFilter.ReconnectPin(Pin: IPin; pmt: PAMMediaType): HRESULT;
- var Graph2: IFilterGraph2;
- begin
- if (FGraph <> nil) then
- begin
- result := FGraph.QueryInterface(IID_IFilterGraph2, Graph2);
- if Succeeded(result) then
- begin
- result := Graph2.ReconnectEx(Pin, pmt);
- Graph2 := nil;
- end
- else
- result := FGraph.Reconnect(Pin);
- end
- else
- result := E_NOINTERFACE;
- end;
- function TBCBaseFilter.Register: HRESULT;
- var
- {$IFDEF DEBUG}
- Filename: array[0..MAX_PATH-1] of Char;
- {$ENDIF}
- FilterMapper : IFilterMapper;
- FilterMapper2: IFilterMapper2;
- Factory: TBCClassFactory;
- AResult : boolean;
- begin
- Aresult := false;
- Result := S_FALSE;
- Factory := Templates.GetFactoryFromClassID(FCLSID);
- if Factory <> nil then
- begin
- {$IFDEF DEBUG}
- GetModuleFileName(hinstance, Filename, sizeof(Filename));
- DbgLog(Self,'Register in ' + Filename);
- {$ENDIF}
- if Failed(CoCreateInstance(CLSID_FilterMapper2, nil, CLSCTX_INPROC_SERVER, IFilterMapper2, FilterMapper2)) then
- if Failed(CoCreateInstance(CLSID_FilterMapper, nil, CLSCTX_INPROC_SERVER, IFilterMapper, FilterMapper)) then exit;
- Factory.UpdateRegistry(false);
- if FilterMapper2 <> nil then
- AResult := Factory.RegisterFilter(FilterMapper2, True)
- else AResult := Factory.RegisterFilter(FilterMapper, True);
- if Aresult then Factory.UpdateRegistry(True);
- FilterMapper := nil;
- FilterMapper2 := nil;
- end;
- if AResult then result := S_OK else result := S_False;
- end;
- function TBCBaseFilter.Run(tStart: TReferenceTime): HRESULT;
- var
- c: integer;
- Pin: TBCBasePin;
- begin
- FLock.Lock;
- try
- // remember the stream time offset
- FStart := tStart;
- if FState = State_Stopped then
- begin
- result := Pause;
- if FAILED(result) then exit;
- end;
- // notify all pins of the change to active state
- if (FState <> State_Running) then
- begin
- for c := 0 to GetPinCount - 1 do
- begin
- Pin := GetPin(c);
- // Disconnected pins are not activated - this saves pins
- // worrying about this state themselves
- if Pin.IsConnected then
- begin
- result := Pin.Run(tStart);
- if FAILED(result) then exit;
- end;
- end;
- end;
- FState := State_Running;
- result := S_OK;
- finally
- FLock.UnLock;
- end;
- end;
- function TBCBaseFilter.SetSyncSource(pClock: IReferenceClock): HRESULT;
- begin
- FLock.Lock;
- try
- FClock := pClock;
- finally
- result := NOERROR;
- FLock.UnLock;
- end;
- end;
- function TBCBaseFilter.Stop: HRESULT;
- var
- c: integer;
- Pin: TBCBasePin;
- hr: HResult;
- begin
- FLock.Lock;
- try
- result := NOERROR;
- // notify all pins of the state change
- if (FState <> State_Stopped) then
- begin
- for c := 0 to GetPinCount - 1 do
- begin
- Pin := GetPin(c);
- // Disconnected pins are not activated - this saves pins worrying
- // about this state themselves. We ignore the return code to make
- // sure everyone is inactivated regardless. The base input pin
- // class can return an error if it has no allocator but Stop can
- // be used to resync the graph state after something has gone bad
- if Pin.IsConnected then
- begin
- hr := Pin.Inactive;
- if (Failed(hr) and SUCCEEDED(result)) then result := hr;
- end;
- end;
- end;
- FState := State_Stopped;
- finally
- FLock.UnLock;
- end;
- end;
- function TBCBaseFilter.StreamTime(out rtStream: TReferenceTime): HRESULT;
- begin
- // Caller must lock for synchronization
- // We can't grab the filter lock because we want to be able to call
- // this from worker threads without deadlocking
- if FClock = nil then
- begin
- result := VFW_E_NO_CLOCK;
- exit;
- end;
- // get the current reference time
- result := FClock.GetTime(PInt64(@rtStream)^);
- if FAILED(result) then exit;
- // subtract the stream offset to get stream time
- rtStream := rtStream - FStart;
- result := S_OK;
- end;
- function TBCBaseFilter.Unregister: HRESULT;
- var
- {$IFDEF DEBUG}
- Filename: array[0..MAX_PATH-1] of Char;
- {$ENDIF}
- FilterMapper : IFilterMapper;
- FilterMapper2: IFilterMapper2;
- Factory: TBCClassFactory;
- AResult : boolean;
- begin
- Aresult := false;
- Result := S_FALSE;
- Factory := Templates.GetFactoryFromClassID(FCLSID);
- if Factory <> nil then
- begin
- {$IFDEF DEBUG}
- GetModuleFileName(hinstance, Filename, sizeof(Filename));
- DbgLog(Self,'Unregister in ' + Filename);
- {$ENDIF}
- if Failed(CoCreateInstance(CLSID_FilterMapper2, nil, CLSCTX_INPROC_SERVER, IFilterMapper2, FilterMapper2)) then
- if Failed(CoCreateInstance(CLSID_FilterMapper, nil, CLSCTX_INPROC_SERVER, IFilterMapper, FilterMapper)) then exit;
- Factory.UpdateRegistry(false);
- if FilterMapper2 <> nil then
- AResult := Factory.RegisterFilter(FilterMapper2, false)
- else AResult := Factory.RegisterFilter(FilterMapper, false);
- if Aresult then Factory.UpdateRegistry(false);
- FilterMapper := nil;
- FilterMapper2 := nil;
- end;
- if AResult then result := S_OK else result := S_False;
- end;
- { TBCEnumPins }
- constructor TBCEnumPins.Create(Filter: TBCBaseFilter; EnumPins: TBCEnumPins);
- var i: integer;
- begin
- FPosition := 0;
- FPinCount := 0;
- FFilter := Filter;
- FPinCache := TList.Create;
- // We must be owned by a filter derived from CBaseFilter
- ASSERT(FFilter <> nil);
- // Hold a reference count on our filter
- FFilter._AddRef;
- // Are we creating a new enumerator
- if (EnumPins = nil) then
- begin
- FVersion := FFilter.GetPinVersion;
- FPinCount := FFilter.GetPinCount;
- end
- else
- begin
- ASSERT(FPosition <= FPinCount);
- FPosition := EnumPins.FPosition;
- FPinCount := EnumPins.FPinCount;
- FVersion := EnumPins.FVersion;
- FPinCache.Clear;
- if EnumPins.FPinCache.Count > 0 then
- for i := 0 to EnumPins.FPinCache.Count - 1 do
- FPinCache.Add(EnumPins.FPinCache.Items[i]);
- end;
- end;
- destructor TBCEnumPins.Destroy;
- begin
- FPinCache.Free;
- FFilter._Release;
- inherited Destroy;
- end;
- function TBCEnumPins.Clone(out ppEnum: IEnumPins): HRESULT;
- begin
- result := NOERROR;
- // Check we are still in sync with the filter
- if AreWeOutOfSync then
- begin
- ppEnum := nil;
- result := VFW_E_ENUM_OUT_OF_SYNC;
- end
- else
- begin
- ppEnum := TBCEnumPins.Create(FFilter, self);
- if ppEnum = nil then result := E_OUTOFMEMORY;
- end;
- end;
- function TBCEnumPins.Next(cPins: ULONG; out ppPins: IPin;
- pcFetched: PULONG): HRESULT;
- type
- TPointerDynArray = array of Pointer;
- TIPinDynArray = array of IPin;
- var
- Fetched: cardinal;
- RealPins: integer;
- Pin: TBCBasePin;
- begin
- if pcFetched <> nil then
- pcFetched^ := 0
- else
- if (cPins>1) then
- begin
- result := E_INVALIDARG;
- exit;
- end;
- Fetched := 0; // increment as we get each one.
- // Check we are still in sync with the filter
- // If we are out of sync, we should refresh the enumerator.
- // This will reset the position and update the other members, but
- // will not clear cache of pins we have already returned.
- if AreWeOutOfSync then
- Refresh;
- // Calculate the number of available pins
- RealPins := min(FPinCount - FPosition, cPins);
- if RealPins = 0 then
- begin
- result := S_FALSE;
- exit;
- end;
- { Return each pin interface NOTE GetPin returns CBasePin * not addrefed
- so we must QI for the IPin (which increments its reference count)
- If while we are retrieving a pin from the filter an error occurs we
- assume that our internal state is stale with respect to the filter
- (for example someone has deleted a pin) so we
- return VFW_E_ENUM_OUT_OF_SYNC }
- while RealPins > 0 do
- begin
- // Get the next pin object from the filter */
- inc(FPosition);
- Pin := FFilter.GetPin(FPosition-1);
- if Pin = nil then
- begin
- // If this happend, and it's not the first time through, then we've got a problem,
- // since we should really go back and release the iPins, which we have previously
- // AddRef'ed.