Modyfikacja filtru PushSource w DirectShow - kłopoty

0

Zmodyfikowałem troszke filtr DirectShow - PushSource. Dodałem interfejs żeby móc wstawiać do strumienia video kolejne bitmapy. Niby wszystko w kodzie jest ok. Filtr sie kompiluje. Jednak gdy chce go użyć w aplikacji, a właściwie zapytać o ten swój nowy interfejs to wyskakuje błąd. Co jest nie tak?
Na razie kod w funkcji Draw nie jest ważny /zmienie go/ chodzi tylko o to zeby mi działał interfejs.

Zmodyfikowany kod PushSource: /na czerwono zmiany/
unit UPushSource;

interface
uses
BaseClass, DirectShow9, DSUtil,
Windows, ActiveX, SysUtils;

const
//
// GUID definitions for PushSource filter set
//
CLSID_PushSourceDXBitmap: TGUID = '{1826E35D-CB50-415A-B8AA-88442A55068E}';
IID_IDXBitmap: TGUID = '{EC769BD3-571C-468C-A112-090ADDC954AF}';

type

IDXBitmap = interface(IUnknown)
['{EC769BD3-571C-468C-A112-090ADDC954AF}']
function Draw: HResult; stdcall;
end;</span>

const
// Setup information
sudPinTypes: TRegPinTypes =
(
// video stream connection
clsMajorType: @MEDIATYPE_Video;
// all available
clsMinorType: @MEDIASUBTYPE_NULL
);

// pins info
sudOutputPinDXBitmap: array[0..0] of TRegFilterPins =
(
(
strName: 'Output'; // Pins string name
bRendered: FALSE; // Is it rendered
bOutput: TRUE; // Is it an output
bZero: FALSE; // Are we allowed none
bMany: FALSE; // And allowed many
oFilter: nil; // Connects to filter
strConnectsToPin: nil; // Connects to pin
nMediaTypes: 1; // Number of types
lpMediaType: @sudPinTypes // Pin information
)
);

UNITS = 10000000;
FPS_30 = UNITS div 30;
FPS_25 = UNITS div 25; //nowa
FPS_20 = UNITS div 20;
FPS_10 = UNITS div 10;
FPS_5 = UNITS div 5;
FPS_4 = UNITS div 4;
FPS_3 = UNITS div 3;
FPS_2 = UNITS div 2;
FPS_1 = UNITS div 1;

DefaultFrameLength: TReferenceTime = FPS_25; //bylo 10

// default bitmap file name
BITMAP_NAME: string = 'klatka.bmp';

type
TBCPushPinDXBitmap = class(TBCSourceStream,IDXBitmap)
protected
// To track where we are in the file
FFramesWritten: Integer;
// Do we need to clear the buffer?
FZeroMemory: Boolean;
// The time stamp for each sample
FSampleTime: TRefTime;

// Pointer to the bitmap header
FBmi: PBitmapInfo;
// Size of the bitmap header
FBitmapInfo: DWord;

// File opening variables
 // Handle returned from CreateFile
FFileHandle: THandle;
// Points to beginning of file buffer
FFileBuffer: PByte;
// Points to pixel bits
FImage: PByte;

// How many frames have been displayed
FFrameNumber: Integer;
// Duration of one frame
FFrameLength: TReferenceTime;

// Protects our internal state
FSharedState: TBCCritSec;

CurrentFileName: AnsiString;

public
constructor Create(out hr: HResult; Filter: TBCSource);
destructor Destroy; override;

// Override the version that offers exactly one media type
function GetMediaType(MediaType: PAMMediaType): HResult; override;
function DecideBufferSize(Allocator: IMemAllocator;
  Properties: PAllocatorProperties): HRESULT; override;
function FillBuffer(Sample: IMediaSample): HResult; override;
// Quality control

// Not implemented because we aren't going in real time.
// If the file-writing filter slows the graph down, we just do nothing, which means
// wait until we're unblocked. No frames are ever dropped.
// Quality control notifications sent to us
function Notify(Filter: IBaseFilter; q: TQuality): HRESULT; override;
stdcall;
//moja
function Draw: HResult; stdcall;
//moja

end;

// In-memory push mode source filter
// Provides a static bitmap as the video output stream.
TBCPushSourceDXBitmap = class(TBCSource)
private
FPin: TBCPushPinDXBitmap;

public
constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);
constructor CreateFromFactory(Factory: TBCClassFactory;
const Controller: IUnknown); override;
destructor Destroy; override;

end;

implementation

// --- TBCPushPinDXBitmap --------------

constructor TBCPushPinDXBitmap.Create(out hr: HResult; Filter: TBCSource);
var
Bufor:array[0..MAX_PATH] of Char;
//CurrentFileName: AnsiString;
FileSize, BytesRead: DWord;
FileHeaderSize: Integer;
BmpFileHeader: PBITMAPFILEHEADER;
pb: PByte;

begin
inherited Create('_ DXBitmap Filter', hr, Filter, 'Out');

FFramesWritten := 0;
FZeroMemory := False;
FBmi := nil;
FBitmapInfo := 0;
FFileHandle := INVALID_HANDLE_VALUE;
FFileBuffer := nil;
FImage := nil;
FFrameNumber := 0;
// Display 25 bitmap frames per second
FFrameLength := FPS_25;
FSharedState := TBCCritSec.Create;

// The main point of this sample is to demonstrate how to take a DIB
// in host memory and insert it into a video stream.
// To keep this sample as simple as possible, we just read a single 24 bpp bitmap
// from a file and copy it into every frame that we send downstream.

// In the filter graph, we connect this filter to the AVI Mux, which creates
// the AVI file with the video frames we pass to it. In this case,
// the end result is a still image rendered as a video stream.

// Your filter will hopefully do something more interesting here.
// The main point is to set up a buffer containing the DIB pixel bits.
// This must be done before you start running.

// First look for the bitmap in the current directory
//GetCurrentDirectory(MAX_PATH - 1, CurrentDir);
//CurrentFileName := Format('%s%s', [CurrentDir, Bitmap_Name]);
GetSystemDirectory(Bufor,sizeof(Bufor));
CurrentFileName := Bufor + '\klatka.bmp';

FFileHandle := CreateFile(PChar(CurrentFileName), GENERIC_READ, 0, nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);

FileSize := GetFileSize(FFileHandle, nil);
if (FileSize = INVALID_FILE_SIZE) then
begin
{$IFDEF DEBUG}
DbgLog(Self, 'Invalid file size');
{$ENDIF}
hr := HRESULTFROMWIN32(GetLastError());
Exit;
end;

FFileBuffer := CoTaskMemAlloc(FileSize);
if (FFileBuffer = nil) then
begin
OutputDebugString('Could not allocate FImage');
hr := E_OUTOFMEMORY;
Exit;
end;

BytesRead := 0;
if not (ReadFile(FFileHandle, FFileBuffer^, FileSize, BytesRead, nil)) then
begin
hr := HRESULTFROMWIN32(GetLastError());
OutputDebugString('ReadFile failed');
Exit;
end;

// WARNING - This code does not verify that the file is a valid bitmap file.
// In your own filter, you would check this or else generate the bitmaps
// yourself in memory.

FileHeaderSize := SizeOf(BITMAPFILEHEADER);

// Store the size of the BITMAPINFO
BmpFileHeader := PBITMAPFILEHEADER(FFileBuffer);
FBitmapInfo := Integer(BmpFileHeader.bfOffBits) - FileHeaderSize;

// Store a pointer to the BITMAPINFO
pb := PByte(FFileBuffer);
Inc(pb, FileHeaderSize);
FBmi := PBITMAPINFO(pb);

// Store a pointer to the starting address of the pixel bits
Inc(pb, FBitmapInfo);
FImage := pb;

// Close and invalidate the file handle, since we have copied its bitmap data
CloseHandle(FFileHandle);
FFileHandle := INVALID_HANDLE_VALUE;
end;

destructor TBCPushPinDXBitmap.Destroy;
begin
{$IFDEF DEBUG}
DbgLog(self, Format('Frames written %d', [FFrameNumber]));
{$ENDIF}

if Assigned(FFileBuffer) then
begin
//FreeMem(FFileBuffer);
CoTaskMemFree(FFileBuffer);
FFileBuffer := nil;
end;

// The constructor might quit early on error and not close the file...
if (FFileHandle <> INVALID_HANDLE_VALUE) then
CloseHandle(FFileHandle);
if (FSharedState <> nil) then
FreeAndNil(FSharedState);

inherited;
end;

// GetMediaType: This method tells the downstream pin what types we support.

// Here is how CSourceStream deals with media types:
//
// If you support exactly one type, override GetMediaType(MediaType : PAMMediaType).
// It will then be called when (a) our filter proposes a media type,
// (b) the other filter proposes a type and we have to check that type.
//
// If you support > 1 type, override GetMediaType(iPosition : Integer;
// out MediaType : PAMMediaType) AND CheckMediaType.
//
// In this case we support only one type, which we obtain from the bitmap file.

function TBCPushPinDXBitmap.GetMediaType(MediaType: PAMMediaType): HResult;
var
pvi: PVIDEOINFOHEADER;

begin
FFilter.StateLock.Lock;
try
if (MediaType = nil) then
begin
Result := E_POINTER;
Exit;
end;

// If the bitmap file was not loaded, just fail here.
if (FImage = nil) then
begin
  Result := E_FAIL;
  Exit;
end;

// Allocate enough room for the VIDEOINFOHEADER and the color tables
MediaType.cbFormat := SIZE_PREHEADER + FBitmapInfo;
pvi := CoTaskMemAlloc(MediaType.cbFormat);
if (pvi = nil) then
begin
  Result := E_OUTOFMEMORY;
  Exit;
end;

ZeroMemory(pvi, MediaType.cbFormat);
pvi.AvgTimePerFrame := FFrameLength;

// Copy the header info
CopyMemory(@pvi.bmiHeader, FBmi, FBitmapInfo);

// Set image size for use in FillBuffer
pvi.bmiHeader.biSizeImage := GetBitmapSize(@pvi.bmiHeader);

// Clear source and target rectangles
// we want the whole image area rendered
SetRectEmpty(pvi.rcSource);
// no particular destination rectangle
SetRectEmpty(pvi.rcTarget);

MediaType.majortype := MEDIATYPE_Video;
MediaType.formattype := FORMAT_VideoInfo;
// Work out the GUID for the subtype from the header info.
MediaType.subtype := GetBitmapSubtype(@pvi.bmiHeader);
MediaType.bTemporalCompression := False;
MediaType.bFixedSizeSamples := True;
MediaType.pbFormat := pvi;
MediaType.lSampleSize := pvi.bmiHeader.biSizeImage;

Result := S_OK;

finally
FFilter.StateLock.UnLock;
end;
end;

function TBCPushPinDXBitmap.DecideBufferSize(Allocator: IMemAllocator;
Properties: PAllocatorProperties): HRESULT;
var
pvi: PVIDEOINFOHEADER;
Actual: ALLOCATOR_PROPERTIES;

begin
if (Allocator = nil) or (Properties = nil) then
begin
Result := E_POINTER;
Exit;
end;

FFilter.StateLock.Lock;
try
// If the bitmap file was not loaded, just fail here.
if (FImage = nil) then
begin
Result := E_FAIL;
Exit;
end;

pvi := AMMediaType.pbFormat;

// Ensure a minimum number of buffers
if (Properties.cBuffers = 0) then
  Properties.cBuffers := 2;
Properties.cbBuffer := pvi.bmiHeader.biSizeImage;

Result := Allocator.SetProperties(Properties^, Actual);
if Failed(Result) then
  Exit;

// Is this allocator unsuitable?
if (Actual.cbBuffer < Properties.cbBuffer) then
  Result := E_FAIL
else
  Result := S_OK;

finally
FFilter.StateLock.UnLock;
end;
end;

// This is where we insert the DIB bits into the video stream.
// FillBuffer is called once for every sample in the stream.

function TBCPushPinDXBitmap.FillBuffer(Sample: IMediaSample): HResult;
var
pData: PByte;
cbData: Longint;
pvi: PVIDEOINFOHEADER;
Start, Stop: REFERENCE_TIME;
{FileSize, BytesRead: DWord;
FileHeaderSize: Integer;
BmpFileHeader: PBITMAPFILEHEADER;
pb: PByte;}

function min(v1, v2: DWord): DWord;
begin
if v1 <= v2 then
Result := v1
else
Result := v2;
end;

begin
if (Sample = nil) then
begin
Result := E_POINTER;
Exit;
end;

// If the bitmap file was not loaded, just fail here.
if (FImage = nil) then
begin
Result := E_FAIL;
Exit;
end;

FSharedState.Lock;
try
// Access the sample's data buffer
Sample.GetPointer(pData);
cbData := Sample.GetSize;

// Check that we're still using video
Assert(IsEqualGUID(AMMediaType.formattype, FORMAT_VideoInfo));

pvi := AMMediaType.pbFormat;

// If we want to change the contents of our source buffer (FImage)
// at some interval or based on some condition, this is where to do it.
// Remember that the new data has the same format that we specified in GetMediaType.
// For example:
// if(FFrameNumber > SomeValue)
//    LoadNewBitsIntoBuffer(FImage)

// Copy the DIB bits over into our filter's output buffer.
// Since sample size may be larger than the image size, bound the copy size.
CopyMemory(pData, FImage, min(pvi.bmiHeader.biSizeImage, cbData));
// Set the timestamps that will govern playback frame rate.
// If this file is getting written out as an AVI,
// then you'll also need to configure the AVI Mux filter to
// set the Average Time Per Frame for the AVI Header.
// The current time is the sample's start
Start := FFrameNumber * FFrameLength;
Stop := Start + FFrameLength;

Sample.SetTime(@Start, @Stop);
Inc(FFrameNumber);

// Set TRUE on every sample for uncompressed frames
Sample.SetSyncPoint(True);

Result := S_OK;

finally
FSharedState.UnLock;
end;
end;

function TBCPushPinDXBitmap.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;
begin
Result := E_FAIL;
end;

function TBCPushPinDXBitmap.Draw: HResult; stdcall;
var
FileSize, BytesRead: DWord;
FileHeaderSize: Integer;
BmpFileHeader: PBITMAPFILEHEADER;
pb: PByte;

begin
Result := S_FALSE;

FSharedState.Lock;
try
FFileHandle := CreateFile(PChar(CurrentFileName),GENERIC_READ,0,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
FileSize := GetFileSize(FFileHandle, nil);
if (FileSize = INVALID_FILE_SIZE) then Exit;
FFileBuffer := CoTaskMemAlloc(FileSize);
if (FFileBuffer = nil) then Exit;
BytesRead := 0;
if not (ReadFile(FFileHandle, FFileBuffer^, FileSize, BytesRead, nil)) then Exit;
FileHeaderSize := SizeOf(BITMAPFILEHEADER);
BmpFileHeader := PBITMAPFILEHEADER(FFileBuffer);
FBitmapInfo := Integer(BmpFileHeader.bfOffBits) - FileHeaderSize;
pb := PByte(FFileBuffer);
Inc(pb, FileHeaderSize);
FBmi := PBITMAPINFO(pb);
Inc(pb, FBitmapInfo);
FImage := pb;
CloseHandle(FFileHandle);
FFileHandle := INVALID_HANDLE_VALUE;

Result := S_OK;

finally
FSharedState.UnLock;
end;
end;</span>

// --- TBCPushSourceDXBitmap ------------

constructor TBCPushSourceDXBitmap.Create(ObjName: string; Unk: IUnKnown;
out hr: HRESULT);
begin
inherited Create(ObjName, Unk, CLSID_PushSourceDXBitmap);

// The pin magically adds itself to our pin array.
FPin := TBCPushPinDXBitmap.Create(hr, Self);

if (hr <> S_OK) then
if (FPin = nil) then
hr := E_OUTOFMEMORY;
end;

constructor TBCPushSourceDXBitmap.CreateFromFactory(Factory: TBCClassFactory;
const Controller: IUnknown);
var
hr: HRESULT;
begin
Create(Factory.Name, Controller, hr);
end;

destructor TBCPushSourceDXBitmap.Destroy;
begin
FreeAndNil(FPin);
inherited;
end;

initialization
// provide entries in the CFactoryTemplate array
TBCClassFactory.CreateFilter(TBCPushSourceDXBitmap,'DXBitmap Filter',
CLSID_PushSourceDXBitmap, CLSID_LegacyAmFilterCategory,
MERIT_DO_NOT_USE, 1, @sudOutputPinDXBitmap
);
end.

Użycie filtru w aplikacji:
...
const
CLSID_PushSourceDXBitmap: TGUID = '{1826E35D-CB50-415A-B8AA-88442A55068E}';
IID_IDXBitmap: TGUID = '{EC769BD3-571C-468C-A112-090ADDC954AF}';

type
IDXBitmap = interface(IUnknown)
['{EC769BD3-571C-468C-A112-090ADDC954AF}']
function Draw: HResult; stdcall;
end;
...
hrr := CoCreateInstance (CLSID_FilterGraph, nil, CLSCTX_INPROC, IID_IGraphBuilder, MyGraphBuilder);
if hrr <> S_OK then
begin
MessageDlg('Nie wykreowano mygraphbuilder.', mtInformation,[mbOk], 0);
Exit;
end;
hrr := CoCreateInstance(CLSID_PushSourceDXBitmap,nil,CLSCTX_INPROC_SERVER,IID_IBaseFilter,Source);
if hrr <> S_OK then
begin
MessageDlg('Nie wykreowano PushSource Bitmap.', mtInformation,[mbOk], 0);
Exit;
end;
MyGraphBuilder.AddFilter(Source,'PushSource');
hrr := CoCreateInstance(CLSID_VideoRenderer,nil,CLSCTX_INPROC_SERVER,IID_IBaseFilter,Renderer);
if hrr <> S_OK then
begin
MessageDlg('Nie wykreowano Renderer.', mtInformation,[mbOk], 0);
Exit;
end;
MyGraphBuilder.AddFilter(Renderer,'Video Renderer');
hrr := GetPin(Renderer,PINDIR_INPUT,RendererIn);
if hrr <> S_OK then
begin
MessageDlg('Nie znaleziono pinu RendererIn.', mtInformation,[mbOk], 0);
Exit;
end;
hrr := GetPin(Source,PINDIR_OUTPUT,SourceOut);
if hrr <> S_OK then
begin
MessageDlg('Nie znaleziono pinu SourceOut.', mtInformation,[mbOk], 0);
Exit;
end;
hrr := MyGraphBuilder.Connect(SourceOut,RendererIn);
if hrr <> S_OK then
begin
MessageDlg('Nie połączono pinu SourceOut i RendererIn.', mtInformation,[mbOk], 0);
Exit;
end;
//tutaj poniżej daje błąd !!!!
hrr := MyGraphBuilder.QueryInterface(IID_IDXBitmap,DXBitmap);
if hrr <> S_OK then
begin
MessageDlg('Nie zapytano o DXBitmap.', mtInformation,[mbOk], 0);
Exit;
end;

0

oczywiście w var jest także deklaracja:

DXBitmap: IDXBitmap;

1 użytkowników online, w tym zalogowanych: 0, gości: 1