📄 main.pas
字号:
unit main;
interface
uses BaseClass, ActiveX, DirectShow9, Windows, DSUTil, PropEdit;
const
CLSID_NullInPlace : TGUID = '{52b63860-dc93-11ce-a099-00aa00479a58}';
IID_INullIPP : TGUID = '{43D849C0-2FE8-11cf-BCB1-444553540000}';
type
INullIPP = interface(IunKnown)
['{0952C77F-2EFF-427B-ACAD-F295ADE6F1E7}']
function put_MediaType(mt: PAM_Media_Type): HRESULT; stdcall; // the media type selected
function get_MediaType(out mt: TAM_Media_Type): HRESULT; stdcall; // the media type selected
function get_IPin(out Pin: IPin): HRESULT; stdcall; // the source pin
function get_State(out State: TFilter_State): HRESULT; stdcall; // the filter state
end;
const
SudPinTypes : TRegPinTypes =
(clsMajorType: @MEDIATYPE_NULL;
clsMinorType: @MEDIASUBTYPE_NULL);
SudPins : array[0..1] of TRegFilterPins =
((strName: 'Input'; bRendered: FALSE; bOutput: FALSE; bZero: FALSE; bMany: FALSE; oFilter: nil; strConnectsToPin: 'Output'; nMediaTypes: 1; lpMediaType: @SudPinTypes),
(strName: 'Output'; bRendered: FALSE; bOutput: TRUE; bZero: FALSE; bMany: FALSE; oFilter: nil; strConnectsToPin: 'Input'; nMediaTypes: 1; lpMediaType: @SudPinTypes));
type
TNullInPlaceInputPin = class(TBCTransInPlaceInputPin)
public
constructor Create(ObjectName: string; TransInPlaceFilter: TBCTransInPlaceFilter;
out hr: HRESULT; Name: WideString);
function CheckMediaType(mt: PAM_Media_Type): HRESULT; override;
end;
TNullInPlaceOutputPin = class(TBCTransInPlaceOutputPin)
public
constructor Create(ObjectName: string; TransInPlaceFilter: TBCTransInPlaceFilter;
out hr: HRESULT; Name: WideString);
function CheckMediaType(mt: PAM_Media_Type): HRESULT; override;
end;
var
// If there are multiple instances of this filter active, it's
// useful for debug messages etc. to know which one this is.
InstanceCount: integer = 0;
type
TNullInPlace = class(TBCTransInPlaceFilter, INullIPP, ISpecifyPropertyPages)
FThisInstance: integer;
FPreferred: TAM_Media_Type; // Media type chosen from property sheet
NullIPLock: TBCCritSec; // To serialise access.
public
function GetPin(n: integer): TBCBasePin; override;
function CheckInputType(mtIn: PAM_Media_Type): HRESULT; override;
function put_MediaType(mt: PAM_Media_Type): HRESULT; stdcall;
function get_MediaType(out mt: TAM_Media_Type): HRESULT; stdcall;
function get_IPin(out Pin: IPin): HRESULT; stdcall;
function get_State(out State: TFilter_State): HRESULT; stdcall; //
// --- ISpecifyPropertyPages ---
function GetPages(out pages: TCAGUID): HResult; stdcall;
constructor Create(ObjName: string; unk: IUnKnown; out hr: HRESULT);
constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
destructor Destroy; override;
// Overrides the PURE virtual Transform of CTransInPlaceFilter base class
// This is where the "real work" is done.
function Transform(Sample: IMediaSample): HRESULT; override;
end;
implementation
{ TNullInPlaceInputPin }
// CheckMediaType
//
// Override CTransInPlaceInputPin method.
// If we have been given a preferred media type from the property sheet
// then only accept a type that is exactly that.
// else if there is nothing downstream, then accept anything
// else if there is a downstream connection then first check to see if
// the subtype (and implicitly the major type) are different from the downstream
// connection and if they are different, fail them
// else ask the downstream input pin if the type (i.e. all details of it)
// are acceptable and take that as our answer.
function TNullInPlaceInputPin.CheckMediaType(mt: PAM_Media_Type): HRESULT;
var
pmt: PAM_Media_Type;
begin
{$IFDEF DEBUG}
DbgLog(self, 'Input type proposed');
{$ENDIF}
pmt := @TNullInPlace(FTIPFilter).FPreferred;
if not TBCMediaType(pmt).IsValid then
begin
if TNullInPlace(FTIPFilter).Output.IsConnected then
begin
// We used to check here if the subtype of the proposed type
// matched the subtype of the type on the output pin
// but this broke as follows:
//
// Renderering the output pin of a CODEC we picked up
// 2 NULLIPs already in the graph:
//
// Subtypes Y41P Y41P RGB565
// Filters CODEC---->NULLIP---->NULLIP------>RENDERER
//
// Each NULLIP has scheduled a reconnect at this point
// and the reconnect on the first connection happens
// first:
//
// Subtypes Y41P RGB565
// Filters CODEC NULLIP---->NULLIP------>RENDERER
//
// In trying to (re)connect the CODEC to the first NULLIP
// we first propose (say) Y41P and the first NULLIP
// checks that Y41P is the same as its output type
// so the call gets passed to the QueryAccept of
// the second NULLIP. The second NULLIP rejected the
// call because the subtype on its output pin is not
// RGB565. In a similar way the first NULLIP
// rejected Y41P.
//
// By removing this optimization (checking the
// subtype before passing the call on) we avoided
// the problem.
result := TNullInPlace(FTIPFilter).Output.GetConnected.QueryAccept(mt^);
exit;
end;
result := S_OK;
exit;
end
else
if TBCMediaType(pmt).Equal(mt) then
begin
result := S_OK;
exit;
end
else
result := VFW_E_TYPE_NOT_ACCEPTED;
end;
constructor TNullInPlaceInputPin.Create(ObjectName: string;
TransInPlaceFilter: TBCTransInPlaceFilter; out hr: HRESULT;
Name: WideString);
begin
inherited Create(ObjectName, TransInPlaceFilter, hr, Name);
end;
{ TNullInPlaceOutputPin }
function TNullInPlaceOutputPin.CheckMediaType(mt: PAM_Media_Type): HRESULT;
var pmt: PAM_Media_Type;
begin
pmt := @TNullInPlace(FTIPFilter).FPreferred;
if not TBCMediaType(pmt).IsValid then
begin
result := inherited CheckMediaType(mt);
exit;
end
else
if TBCMediaType(pmt).Equal(mt) then
begin
result := S_OK;
exit;
end
else
result := VFW_E_TYPE_NOT_ACCEPTED;
end;
constructor TNullInPlaceOutputPin.Create(ObjectName: string;
TransInPlaceFilter: TBCTransInPlaceFilter; out hr: HRESULT;
Name: WideString);
begin
inherited Create(ObjectName, TransInPlaceFilter, hr, Name);
end;
{ TNullInPlace }
function TNullInPlace.CheckInputType(mtIn: PAM_Media_Type): HRESULT;
begin
result := S_OK;
end;
constructor TNullInPlace.Create(ObjName: string; unk: IUnKnown;
out hr: HRESULT);
var pmt: PAM_Media_Type;
begin
inherited Create(ObjName, unk, CLSID_NullInPlace, hr);
FThisInstance := InterlockedIncrement(InstanceCount);
pmt := @FPreferred;
TBCMediaType(pmt).InitMediaType;
NullIPLock := TBCCritSec.Create;
DbgLog(self, 'TNullInPlace.Create');
end;
constructor TNullInPlace.CreateFromFactory(Factory: TBCClassFactory;
const Controller: IUnKnown);
var hr: HRESULT;
begin
Create(Factory.Name, Controller, hr);
end;
destructor TNullInPlace.Destroy;
begin
NullIPLock.Free;
inherited;
end;
function TNullInPlace.get_IPin(out Pin: IPin): HRESULT;
begin
result := S_OK;
NullIPLock.Lock;
try
if (Input = nil) then
begin
Pin := nil;
exit;
end;
if not Input.IsConnected then
Pin := nil
else Pin := Input.GetConnected;
finally
NullIPLock.UnLock;
end;
end;
function TNullInPlace.get_MediaType(out mt: TAM_Media_Type): HRESULT;
begin
NullIPLock.Lock;
try
mt := FPreferred;
result := NOERROR ;
finally
NullIPLock.UnLock;
end;
end;
function TNullInPlace.get_State(out State: TFilter_State): HRESULT;
begin
NullIPLock.Lock;
try
State := self.State;
result := NOERROR ;
finally
NullIPLock.UnLock;
end;
end;
function TNullInPlace.GetPages(out pages: TCAGUID): HResult;
begin
Pages.cElems := 1;
Pages.pElems := CoTaskMemAlloc(sizeof(TGUID));
if (Pages.pElems = nil) then
begin
result := E_OUTOFMEMORY;
exit;
end;
Pages.pElems^[0] := CLSID_NullIPPropertyPage;
result := NOERROR;
end;
function TNullInPlace.GetPin(n: integer): TBCBasePin;
var hr: HRESULT;
begin
// Create the single input pin and the single output pin
// If anything fails, fail the whole lot and clean up.
if (Input = nil) or (Output = nil) then
begin
hr := S_OK;
Input := TNullInPlaceInputPin.Create('Null input pin', self, hr, 'Input');
// a failed return code should delete the object
if FAILED(hr) or (Input = nil) then
begin
if (Input <> nil) then input.Free;
input := nil;
result := nil;
exit;
end;
Output := TNullInPlaceOutputPin.Create('Null output pin', self, hr, 'Output');
// failed return codes cause both objects to be deleted
if FAILED(hr) or (Output = nil) then
begin
if (Input <> nil) then input.Free;
if (Output <> nil) then Output.Free;
Input := nil;
Output := nil;
result := nil;
exit;
end;
end;
// Find which pin is required
case n of
0: result := Input;
1: result := Output;
else
result := nil;
end;
end;
function TNullInPlace.put_MediaType(mt: PAM_Media_Type): HRESULT;
var
Pin: IPin;
pmt: PAM_Media_Type;
begin
NullIPLock.Lock;
try
// if the state of the graph is running, fail the call.
if (State = State_Running) then
begin
result := E_UNEXPECTED;
exit;
end;
// check the source and sink filters like this media type
pmt := @FPreferred;
if (mt = nil) then
TBCMediaType(pmt).InitMediaType
else
begin
Pin := Input.GetConnected;
if (Pin <> nil) then
begin
if (Pin.QueryAccept(mt^) <> NOERROR) then
begin
MessageBox(0,PChar('Upstream filter cannot provide this type'),
PChar('Format Selection'),
MB_OK or MB_ICONEXCLAMATION);
result := VFW_E_TYPE_NOT_ACCEPTED;
exit;
end;
end;
Pin := Output.GetConnected;
if (Pin <> nil) then
begin
if (Pin.QueryAccept(mt^) <> NOERROR) then
begin
MessageBox(0, PChar('Downstream filter cannot accept this type'),
PChar('Format Selection'),
MB_OK or MB_ICONEXCLAMATION);
result := VFW_E_TYPE_NOT_ACCEPTED;
exit;
end;
end;
FPreferred := mt^;
end;
// force reconnect of input if the media type of connection does not match.
if (Input.IsConnected) then
begin
pmt := Input.CurrentMediaType.MediaType;
if not TBCMediaType(pmt).Equal(@FPreferred) then
Graph.Reconnect(Input);
end;
result := NOERROR ;
finally
NullIPLock.Unlock;
end;
end;
function TNullInPlace.Transform(Sample: IMediaSample): HRESULT;
begin
result := S_OK;
end;
initialization
TBCClassFactory.CreateFilter(TNullInPlace, 'Null-In-Place', CLSID_NullInPlace,
CLSID_LegacyAmFilterCategory, MERIT_DO_NOT_USE, 2, @SudPins);
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -