UPushSource.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:45k
- //------------------------------------------------------------------------------
- // File: UPushSource.pas
- // Original file(s): Setup.cpp, PushSource.h,
- // PushSourceBitmap.cpp, PushSourceBitmapSet.cpp,
- // PushSourceDesktop.cpp, PushSource.h,
- // DibHelper.cpp, DibHelper.h, PushGuids.h
- // Converted to Delphi by Nevhasymyy Andriy (E-Mail: a.n@email.com)
- // Thanks to Henri Gourvest (hgourvest@progdigy.com)
- //
- // Desc: DirectShow sample code - In-memory push mode source filter
- //
- // Portions created by Microsoft are
- // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------------------------
- {
- @abstract(In-memory push mode source filter Delphi conversion)
- @author(Andriy Nevhasymyy: a.n@email.com)
- @created(Jun 28, 2003)
- @lastmod(Aug 04, 2003)
- }
- unit UPushSource;
- interface
- uses
- BaseClass, DirectShow9, DSUtil,
- Windows, ActiveX, SysUtils;
- const
- //
- // GUID definitions for PushSource filter set
- //
- CLSID_PushSourceBitmap: TGUID = '{E446D455-7C13-492A-9C96-38F948687E8A}';
- CLSID_PushSourceBitmapSet: TGUID = '{D5070569-3C5F-4988-A77C-4DF44E850C43}';
- CLSID_PushSourceDesktop: TGUID = '{570757C1-D2D8-42D1-BA0C-24E1BED3F62F}';
- // Setup information
- sudPinTypes: TRegPinTypes =
- (
- // video stream connection
- clsMajorType: @MEDIATYPE_Video;
- // all available
- clsMinorType: @MEDIASUBTYPE_NULL
- );
- // pins info
- sudOutputPinBitmap: 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
- )
- );
- sudOutputPinBitmapSet: 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
- )
- );
- sudOutputPinDesktop: 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_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_10;
- // Filter name strings
- PushBitmapName: WideString = '_ PushSource Bitmap Filter';
- PushBitmapSetName: WideString = '_ PushSource BitmapSet Filter';
- PushDesktopName: WideString = '_ PushSource Desktop Filter';
- // default bitmap file name
- BITMAP_NAME: string = 'sample.bmp';
- // Number of bitmap files to load in the CPushPinBitmapSet class
- Num_Files = 5;
- type
- (**********************************************
- *
- * Class declarations
- *
- **********************************************)
- TBCPushPinBitmap = class(TBCSourceStream)
- 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;
- 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;
- end;
- TBCPushPinBitmapSet = class(TBCSourceStream)
- 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;
- // Array of bitmap headers pointers
- FBmi: array[0..Num_Files - 1] of PBitmapInfo;
- // Sizes of the bitmap headers
- FBitmapInfo: array[0..Num_Files - 1] of DWord;
- // File opening variables
- // Handles returned from CreateFile
- FFileHandle: array[0..Num_Files - 1] of THandle;
- // Points to beginning of files buffer
- FFileBuffer: array[0..Num_Files - 1] of PByte;
- // Points to pixel bits
- FImage: array[0..Num_Files - 1] of PByte;
- // number of files loaded
- FFilesLoaded: Boolean;
- // Which bitmap is being displayed
- FCurrentBitmap,
- // How many frames have been displayed
- FFrameNumber: Integer;
- // Duration of one frame
- FFrameLength: TReferenceTime;
- // Protects our internal state
- FSharedState: TBCCritSec;
- 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;
- end;
- TBCPushPinDesktop = class(TBCSourceStream)
- 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;
- // How many frames have been displayed
- FFrameNumber: Integer;
- // Duration of one frame
- FFrameLength: TReferenceTime;
- // Rect containing entire screen coordinates
- FScreenRect: TRect;
- // The current image height
- FImageHeight,
- // And current image width
- FImageWidth,
- // Time in msec between frames
- FRepeatTime,
- // Screen bit depth
- FCurrentBitDepth: Integer;
- FMediaType: TAMMediaType;
- // Protects our internal state
- FSharedState: TBCCritSec;
- public
- constructor Create(out hr: HResult; Filter: TBCSource);
- destructor Destroy; override;
- // Override the version that offers exactly one media type
- function GetMediaType(iPosition: Integer;
- out MediaType: PAMMediaType): HResult; override;
- // We will accept 8, 16, 24 or 32 bit video formats, in any
- // image size that gives room to bounce.
- // Returns E_INVALIDARG if the mediatype is not acceptable
- function CheckMediaType(MediaType: PAMMediaType): HResult; override;
- function DecideBufferSize(Allocator: IMemAllocator;
- Properties: PAllocatorProperties): HRESULT; override;
- function SetMediaType(MediaType: PAMMediaType): 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;
- end;
- // In-memory push mode source filter
- // Provides a static bitmap as the video output stream.
- TBCPushSourceBitmap = class(TBCSource)
- private
- FPin: TBCPushPinBitmap;
- public
- constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);
- constructor CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnknown); override;
- destructor Destroy; override;
- end;
- // In-memory push mode source filter
- // Provides a rotating set of bitmaps as the video output stream.
- TBCPushSourceBitmapset = class(TBCSource)
- private
- FPin: TBCPushPinBitmapSet;
- public
- constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);
- constructor CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnknown); override;
- destructor Destroy; override;
- end;
- // In-memory push mode source filter
- // Provides an image of the user's desktop as a continuously updating stream.
- TBCPushSourceDesktop = class(TBCSource)
- private
- FPin: TBCPushPinDesktop;
- public
- constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);
- constructor CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnknown); override;
- destructor Destroy; override;
- end;
- implementation
- // --- TBCPushPinBitmap ---------------
- constructor TBCPushPinBitmap.Create(out hr: HResult; Filter: TBCSource);
- var
- CurrentDir: array[0..MAX_PATH - 1] of Char;
- CurrentFileName, MediaFileName: AnsiString;
- MsgText: AnsiString;
- FileSize, BytesRead: DWord;
- FileHeaderSize: Integer;
- BmpFileHeader: PBITMAPFILEHEADER;
- pb: PByte;
- begin
- inherited Create('_ Push Source Bitmap', hr, Filter, 'Out');
- FFramesWritten := 0;
- FZeroMemory := False;
- FBmi := nil;
- FBitmapInfo := 0;
- FFileHandle := INVALID_HANDLE_VALUE;
- FFileBuffer := nil;
- FImage := nil;
- FFrameNumber := 0;
- // Display 5 bitmap frames per second
- FFrameLength := FPS_5;
- 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]);
- FFileHandle := CreateFile(PChar(CurrentFileName), GENERIC_READ, 0, nil,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, 0);
- if (FFileHandle = INVALID_HANDLE_VALUE) then
- begin
- // File was not in the application's current directory,
- // so look in the DirectX SDK media path instead.
- MediaFileName := Format('%s%s'#0, [GetDXSDKMediaPath, Bitmap_Name]);
- FFileHandle := CreateFile(PChar(MediaFileName), GENERIC_READ, 0, nil,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, 0);
- if (FFileHandle = INVALID_HANDLE_VALUE) then
- begin
- MsgText := Format('Could not open bitmap source file ' +
- 'in the application directory:'#13#10 + '%s'#13#10 +
- 'or in the DirectX SDK Media folder:'#13#10 + '%s'#13#10 +
- 'Please copy this file either to the application''s folder'#13#10 +
- 'or to the DirectX SDK Media folder, then recreate this filter'#13#10 +
- 'Otherwise, you will not be able to render the output pin',
- [CurrentFileName, MediaFileName]);
- OutputDebugString(PChar(MsgText));
- MessageBox(0, PChar(MsgText), 'PushSource filter error',
- MB_ICONERROR or MB_OK);
- hr := HRESULTFROMWIN32(GetLastError);
- Exit;
- end;
- end;
- 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 TBCPushPinBitmap.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 TBCPushPinBitmap.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 TBCPushPinBitmap.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 TBCPushPinBitmap.FillBuffer(Sample: IMediaSample): HResult;
- var
- pData: PByte;
- cbData: Longint;
- pvi: PVIDEOINFOHEADER;
- Start, Stop: REFERENCE_TIME;
- 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 TBCPushPinBitmap.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;
- begin
- Result := E_FAIL;
- end;
- // --- TBCPushSourceBitmap ------------
- constructor TBCPushSourceBitmap.Create(ObjName: string; Unk: IUnKnown;
- out hr: HRESULT);
- begin
- inherited Create(ObjName, Unk, CLSID_PushSourceBitmap);
- // The pin magically adds itself to our pin array.
- FPin := TBCPushPinBitmap.Create(hr, Self);
- if (hr <> S_OK) then
- if (FPin = nil) then
- hr := E_OUTOFMEMORY;
- end;
- constructor TBCPushSourceBitmap.CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnknown);
- var
- hr: HRESULT;
- begin
- Create(Factory.Name, Controller, hr);
- end;
- destructor TBCPushSourceBitmap.Destroy;
- begin
- FreeAndNil(FPin);
- inherited;
- end;
- // --- TBCPushPinBitmapSet ------------
- constructor TBCPushPinBitmapSet.Create(out hr: HResult; Filter: TBCSource);
- var
- CurrentDir: array[0..MAX_PATH - 1] of Char;
- CurrentFileName, MediaFileName: AnsiString;
- MsgText: AnsiString;
- FileSize, BytesRead: DWord;
- FilesLoaded, FileHeaderSize: Integer;
- BmpFileHeader: PBITMAPFILEHEADER;
- pb: PByte;
- i: Integer;
- begin
- inherited Create('_ Push Source Bitmap Set', hr, Filter, 'Out');
- FFramesWritten := 0;
- FZeroMemory := False;
- FFrameNumber := 0;
- // Display 5 bitmap frames per second
- FFrameLength := FPS_2;
- FSharedState := TBCCritSec.Create;
- FCurrentBitmap := 0;
- FFilesLoaded := False;
- FilesLoaded := 0;
- // Initialize member data arrays
- ZeroMemory(@FBitmapInfo, NUM_FILES * sizeof(DWord));
- ZeroMemory(@FBmi, NUM_FILES * sizeof(PBitmapInfo));
- ZeroMemory(@FFileHandle, NUM_FILES * sizeof(THandle));
- ZeroMemory(@FFileBuffer, NUM_FILES * sizeof(Byte));
- ZeroMemory(@FImage, NUM_FILES * sizeof(Byte));
- // The main point of this sample is to demonstrate how to take a DIB
- // in host memory and insert it into a video stream.
- // We read a set of bitmaps from files and copy one bitmap
- // 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 rotating set of images rendered as a video stream.
- // First look for the bitmap in the current directory
- GetCurrentDirectory(MAX_PATH - 1, CurrentDir);
- for i := 0 to NUM_FILES - 1 do
- begin
- // Assume that the bitmap in the application's directory
- CurrentFileName := Format('%sBitmapSet%d.bmp', [CurrentDir, i]);
- FFileHandle[i] := CreateFile(PChar(CurrentFileName), GENERIC_READ, 0, nil,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if (FFileHandle[i] = INVALID_HANDLE_VALUE) then
- begin
- // File was not in the application's current directory,
- // so look in the DirectX SDK media path instead. The path contained
- // in szMediaDir will already have a trailing backslash ''.
- MediaFileName := Format('%sBitmapSet%d.bmp', [GetDXSDKMediaPath, i]);
- FFileHandle[i] := CreateFile(PChar(MediaFileName), GENERIC_READ, 0, nil,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if (FFileHandle[i] = INVALID_HANDLE_VALUE) then
- begin
- MsgText := Format('Could not open bitmap source file (#%d of %d) ' +
- 'in the application directory:'#13#10 + '%s'#13#10 +
- 'or in the DirectX SDK Media folder:'#13#10 + '%s'#13#10 +
- 'Please copy this file either to the application''s folder'#13#10 +
- 'or to the DirectX SDK Media folder, then recreate this filter'#13#10 +
- 'Otherwise, you will not be able to render the output pin',
- [i + 1, NUM_FILES, CurrentFileName, MediaFileName]);
- OutputDebugString(PChar(MsgText));
- MessageBox(0, PChar(MsgText), 'PushSource filter error',
- MB_ICONERROR or MB_OK);
- hr := HRESULTFROMWIN32(GetLastError());
- Exit;
- end;
- end;
- FileSize := GetFileSize(FFileHandle[i], nil);
- if (FileSize = INVALID_FILE_SIZE) then
- begin
- {$IFDEF DEBUG}
- DbgLog(Self, 'Invalid file size');
- {$ENDIF}
- hr := HRESULTFROMWIN32(GetLastError());
- Exit;
- end;
- FFileBuffer[i] := CoTaskMemAlloc(FileSize);
- if (FFileBuffer[i] = nil) then
- begin
- OutputDebugString('Could not allocate FImage');
- hr := E_OUTOFMEMORY;
- Exit;
- end;
- BytesRead := 0;
- if not (ReadFile(FFileHandle[i], FFileBuffer[i]^, 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[i]);
- FBitmapInfo[i] := Integer(BmpFileHeader.bfOffBits) - FileHeaderSize;
- // Store a pointer to the BITMAPINFO
- pb := PByte(FFileBuffer[i]);
- Inc(pb, FileHeaderSize);
- FBmi[i] := PBITMAPINFO(pb);
- // Store a pointer to the starting address of the pixel bits
- Inc(pb, FBitmapInfo[i]);
- FImage[i] := pb;
- // Close and invalidate the file handle, since we have copied its bitmap data
- CloseHandle(FFileHandle[i]);
- FFileHandle[i] := INVALID_HANDLE_VALUE;
- // Count this is a successful file load. If not all files load
- // properly, then the filter will not operate correctly.
- Inc(FilesLoaded);
- end;
- // Make sure that ALL files were properly loaded
- if (FilesLoaded <> NUM_FILES) then
- hr := E_FAIL
- else
- begin
- FFilesLoaded := TRUE;
- end;
- end;
- destructor TBCPushPinBitmapSet.Destroy;
- var
- i: Integer;
- begin
- {$IFDEF DEBUG}
- DbgLog(self, Format('Frames written %d', [FFrameNumber]));
- {$ENDIF}
- for i := 0 to NUM_FILES - 1 do
- begin
- if Assigned(FFileBuffer[i]) then
- begin
- CoTaskMemFree(FFileBuffer[i]);
- FFileBuffer[i] := nil;
- end;
- // The constructor might quit early on error and not close the file...
- if (FFileHandle[i] <> INVALID_HANDLE_VALUE) then
- CloseHandle(FFileHandle[i]);
- end;
- 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 TBCPushPinBitmapSet.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 files were not loaded, just fail here.
- if not (FFilesLoaded) then
- begin
- Result := E_FAIL;
- Exit;
- end;
- // Allocate enough room for the VIDEOINFOHEADER and the color tables
- MediaType.cbFormat := SIZE_PREHEADER + FBitmapInfo[FCurrentBitmap];
- 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[FCurrentBitmap],
- FBitmapInfo[FCurrentBitmap]);
- // 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.cbFormat := SizeOf(TBitmapInfo);
- MediaType.pbFormat := pvi;
- MediaType.lSampleSize := pvi.bmiHeader.biSizeImage;
- Result := S_OK;
- finally
- FFilter.StateLock.UnLock;
- end;
- end;
- function TBCPushPinBitmapSet.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 files were not loaded, just fail here.
- if not (FFilesLoaded) 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 TBCPushPinBitmapSet.FillBuffer(Sample: IMediaSample): HResult;
- var
- pData: PByte;
- cbData: Longint;
- pvi: PVIDEOINFOHEADER;
- Start, Stop: REFERENCE_TIME;
- function min(v1, v2: DWord): DWord;
- begin
- if v1 <= v2 then
- Result := v1
- else
- Result := v2;
- end;
- begin
- // If the bitmap files were not loaded, just fail here.
- if not (FFilesLoaded) then
- begin
- Result := E_FAIL;
- Exit;
- end;
- if (Sample = nil) then
- begin
- Result := E_POINTER;
- 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;
- // 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.
- // Remember that the new data has the same format
- // that we specified in GetMediaType.
- CopyMemory(pData, FImage[FCurrentBitmap],
- 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);
- // Increment the current buffer so that the next FillBuffer() call
- // will use the bits from the next bitmap in the set.
- Inc(FCurrentBitmap);
- FCurrentBitmap := FCurrentBitmap mod NUM_FILES;
- Result := S_OK;
- finally
- FSharedState.UnLock;
- end;
- end;
- function TBCPushPinBitmapSet.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;
- begin
- Result := E_FAIL;
- end;
- (**********************************************
- *
- * CPushSourceBitmapSet Class
- *
- **********************************************)
- constructor TBCPushSourceBitmapSet.Create(ObjName: string; Unk: IUnKnown;
- out hr: HRESULT);
- begin
- inherited Create(ObjName, Unk, CLSID_PushSourceBitmapSet);
- // The pin magically adds itself to our pin array.
- FPin := TBCPushPinBitmapSet.Create(hr, Self);
- if (hr <> S_OK) then
- if (FPin = nil) then
- hr := E_OUTOFMEMORY;
- end;
- constructor TBCPushSourceBitmapSet.CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnknown);
- var
- hr: HRESULT;
- begin
- Create(Factory.Name, Controller, hr);
- end;
- destructor TBCPushSourceBitmapSet.Destroy;
- begin
- FreeAndNil(FPin);
- inherited;
- end;
- // --- TBCPushPinDesktop --------------
- constructor TBCPushPinDesktop.Create(out hr: HResult; Filter: TBCSource);
- var
- DC: HDC;
- begin
- inherited Create('_ Push Source Desktop', hr, Filter, 'Out');
- FFramesWritten := 0;
- FZeroMemory := False;
- FFrameNumber := 0;
- // Display 5 bitmap frames per second
- FFrameLength := FPS_5;
- FSharedState := TBCCritSec.Create;
- FCurrentBitDepth := 32;
- // 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 the desktop image
- // 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 screen capture video (GDI images only, with no
- // support for overlay surfaces).
- // Get the device context of the main display
- DC := CreateDC('DISPLAY', nil, nil, nil);
- // Get the dimensions of the main desktop window
- FScreenRect.Left := 0;
- FScreenRect.Top := 0;
- FScreenRect.Right := GetDeviceCaps(DC, HORZRES);
- FScreenRect.Bottom := GetDeviceCaps(DC, VERTRES);
- // Save dimensions for later use in FillBuffer()
- FImageWidth := FScreenRect.Right - FScreenRect.Left;
- FImageHeight := FScreenRect.Bottom - FScreenRect.Top;
- // Release the device context
- DeleteDC(DC);
- hr := S_OK;
- end;
- destructor TBCPushPinDesktop.Destroy;
- begin
- {$IFDEF DEBUG}
- DbgLog(self, Format('Frames written %d', [FFrameNumber]));
- {$ENDIF}
- inherited;
- end;
- // GetMediaType
- //
- // Prefer 5 formats - 8, 16 (*2), 24 or 32 bits per pixel
- //
- // Prefered types should be ordered by quality, with zero as highest quality.
- // Therefore, iPosition =
- // 0 Return a 32bit mediatype
- // 1 Return a 24bit mediatype
- // 2 Return 16bit RGB565
- // 3 Return a 16bit mediatype (rgb555)
- // 4 Return 8 bit palettised format
- // >4 Invalid
- //
- function TBCPushPinDesktop.GetMediaType(iPosition: Integer;
- out MediaType: PAMMediaType): HResult;
- var
- pvi: PVIDEOINFO;
- i: Integer;
- begin
- FFilter.StateLock.Lock;
- try
- if (MediaType = nil) then
- begin
- Result := E_POINTER;
- Exit;
- end;
- if (iPosition < 0) then
- begin
- Result := E_INVALIDARG;
- Exit;
- end;
- // Have we run off the end of types?
- if (iPosition > 4) then
- begin
- Result := VFW_S_NO_MORE_ITEMS;
- Exit;
- end;
- MediaType.cbFormat := SizeOf(TVideoInfo);
- pvi := CoTaskMemAlloc(MediaType.cbFormat);
- if (pvi = nil) then
- begin
- Result := E_OUTOFMEMORY;
- Exit;
- end;
- // Initialize the VideoInfo structure before configuring its members
- ZeroMemory(pvi, MediaType.cbFormat);
- case iPosition of
- 0:
- begin
- // Return our highest quality 32bit format
- // Since we use RGB888 (the default for 32 bit), there is
- // no reason to use BI_BITFIELDS to specify the RGB
- // masks. Also, not everything supports BI_BITFIELDS
- pvi.bmiHeader.biCompression := BI_RGB;
- pvi.bmiHeader.biBitCount := 32;
- end;
- 1:
- begin
- // Return our 24bit format
- pvi.bmiHeader.biCompression := BI_RGB;
- pvi.bmiHeader.biBitCount := 24;
- end;
- 2:
- begin
- // 16 bit per pixel RGB565
- // Place the RGB masks as the first 3 doublewords in the palette area
- for i := 0 to 2 do
- pvi.TrueColorInfo.dwBitMasks[i] := bits565[i];
- pvi.bmiHeader.biCompression := BI_BITFIELDS;
- pvi.bmiHeader.biBitCount := 16;
- end;
- 3:
- begin
- // 16 bits per pixel RGB555
- // Place the RGB masks as the first 3 doublewords in the palette area
- for i := 0 to 2 do
- pvi.TrueColorInfo.dwBitMasks[i] := bits555[i];
- pvi.bmiHeader.biCompression := BI_BITFIELDS;
- pvi.bmiHeader.biBitCount := 16;
- end;
- 4:
- begin
- // 8 bit palettised
- pvi.bmiHeader.biCompression := BI_RGB;
- pvi.bmiHeader.biBitCount := 8;
- pvi.bmiHeader.biClrUsed := iPALETTE_COLORS;
- end;
- end;
- // Adjust the parameters common to all formats
- pvi.bmiHeader.biSize := SizeOf(TBitmapInfoHeader);
- pvi.bmiHeader.biWidth := FImageWidth;
- pvi.bmiHeader.biHeight := FImageHeight;
- pvi.bmiHeader.biPlanes := 1;
- pvi.bmiHeader.biSizeImage := GetBitmapSize(@pvi.bmiHeader);
- pvi.bmiHeader.biClrImportant := 0;
- // 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;
- MediaType.bTemporalCompression := False;
- MediaType.bFixedSizeSamples := True;
- // Work out the GUID for the subtype from the header info.
- MediaType.subtype := GetBitmapSubtype(@pvi.bmiHeader);
- MediaType.pbFormat := pvi;
- MediaType.lSampleSize := pvi.bmiHeader.biSizeImage;
- Result := S_OK;
- finally
- FFilter.StateLock.UnLock;
- end;
- end;
- // We will accept 8, 16, 24 or 32 bit video formats, in any
- // image size that gives room to bounce.
- // Returns E_INVALIDARG if the mediatype is not acceptable
- function TBCPushPinDesktop.CheckMediaType(MediaType: PAMMediaType): HResult;
- var
- pvi: PVIDEOINFO;
- SubType: TGUID;
- begin
- // we only output video
- if not (IsEqualGUID(MediaType.majortype, MEDIATYPE_Video)) or
- // in fixed size samples
- not (MediaType.bFixedSizeSamples) then
- begin
- Result := E_INVALIDARG;
- Exit;
- end;
- // Check for the subtypes we support
- SubType := MediaType.subtype;
- if IsEqualGUID(SubType, GUID_NULL) then
- begin
- Result := E_INVALIDARG;
- Exit;
- end;
- if not (
- IsEqualGUID(SubType, MEDIASUBTYPE_RGB8) or
- IsEqualGUID(SubType, MEDIASUBTYPE_RGB565) or
- IsEqualGUID(SubType, MEDIASUBTYPE_RGB555) or
- IsEqualGUID(SubType, MEDIASUBTYPE_RGB24) or
- IsEqualGUID(SubType, MEDIASUBTYPE_RGB32)
- ) then
- begin
- Result := E_INVALIDARG;
- Exit;
- end;
- // Get the format area of the media type
- pvi := MediaType.pbFormat;
- if (pvi = nil) then
- begin
- Result := E_INVALIDARG;
- Exit;
- end;
- // Check if the image width & height have changed
- if (pvi.bmiHeader.biWidth <> FImageWidth) or
- (abs(pvi.bmiHeader.biHeight) <> FImageHeight) then
- // If the image width/height is changed, fail CheckMediaType() to force
- // the renderer to resize the image.
- begin
- Result := E_INVALIDARG;
- Exit;
- end;
- // Don't accept formats with negative height, which would cause the desktop
- // image to be displayed upside down.
- if (pvi.bmiHeader.biHeight < 0) then
- begin
- Result := E_INVALIDARG;
- Exit;
- end;
- Result := S_OK; // This format is acceptable.
- end;
- //
- // DecideBufferSize
- //
- // This will always be called after the format has been sucessfully
- // negotiated. So we have a look at AMMediaType to see what size image we agreed.
- // Then we can ask for buffers of the correct size to contain them.
- //
- function TBCPushPinDesktop.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
- pvi := AMMediaType.pbFormat;
- Properties.cBuffers := 1;
- Properties.cbBuffer := pvi.bmiHeader.biSizeImage;
- Assert(Properties.cbBuffer <> 0);
- // Ask the allocator to reserve us some sample memory. NOTE: the function
- // can succeed (return NOERROR) but still not have allocated the
- // memory that we requested, so we must check we got whatever we wanted.
- Result := Allocator.SetProperties(Properties^, Actual);
- if Failed(Result) then
- Exit;
- // Is this allocator unsuitable?
- if (Actual.cbBuffer < Properties.cbBuffer) then
- begin
- Result := E_FAIL;
- Exit;
- end;
- // Make sure that we have only 1 buffer
- Assert(Actual.cBuffers = 1);
- Result := S_OK;
- finally
- FFilter.StateLock.UnLock;
- end;
- end;
- //
- // SetMediaType
- //
- // Called when a media type is agreed between filters
- //
- function TBCPushPinDesktop.SetMediaType(MediaType: PAMMediaType): HRESULT;
- var
- pvi: PVIDEOINFOHEADER;
- begin
- FFilter.StateLock.Lock;
- try
- // Pass the call up to my base class
- Result := inherited SetMediaType(MediaType);
- if Succeeded(Result) then
- begin
- pvi := AMMediaType.pbFormat;
- if (pvi = nil) then
- begin
- Result := E_UNEXPECTED;
- Exit;
- end;
- // 8-bit palettized,
- // RGB565, RGB555,
- // RGB24,
- // RGB32
- if pvi.bmiHeader.biBitCount in [8, 16, 24, 32] then
- begin
- // Save the current media type and bit depth
- FMediaType := MediaType^;
- FCurrentBitDepth := pvi.bmiHeader.biBitCount;
- end
- else
- begin
- // We should never agree any other media types
- Assert(False);
- Result := E_INVALIDARG;
- end;
- end;
- 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 TBCPushPinDesktop.FillBuffer(Sample: IMediaSample): HResult;
- var
- pData: PByte;
- cbData: Longint;
- hDib: HBitmap;
- pvih: PVIDEOINFOHEADER;
- Start, Stop: REFERENCE_TIME;
- 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;
- 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));
- pvih := AMMediaType.pbFormat;
- // 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.
- pVih.bmiHeader.biSizeImage := min(pVih.bmiHeader.biSizeImage, cbData);
- hDib := CopyScreenToBitmap(FScreenRect, pData, @pVih.bmiHeader);
- if (hDib <> 0) then
- DeleteObject(hDib);
- // 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 TBCPushPinDesktop.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;
- begin
- Result := E_FAIL;
- end;
- // --- TBCPushSourceBitmap ------------
- constructor TBCPushSourceDesktop.Create(ObjName: string; Unk: IUnKnown;
- out hr: HRESULT);
- begin
- inherited Create(ObjName, Unk, CLSID_PushSourceDesktop);
- // The pin magically adds itself to our pin array.
- FPin := TBCPushPinDesktop.Create(hr, Self);
- if (hr <> S_OK) then
- if (FPin = nil) then
- hr := E_OUTOFMEMORY;
- end;
- constructor TBCPushSourceDesktop.CreateFromFactory(Factory: TBCClassFactory;
- const Controller: IUnknown);
- var
- hr: HRESULT;
- begin
- Create(Factory.Name, Controller, hr);
- end;
- destructor TBCPushSourceDesktop.Destroy;
- begin
- FreeAndNil(FPin);
- inherited;
- end;
- initialization
- // provide entries in the CFactoryTemplate array
- TBCClassFactory.CreateFilter(TBCPushSourceBitmap, PushBitmapName,
- CLSID_PushSourceBitmap, CLSID_LegacyAmFilterCategory,
- MERIT_DO_NOT_USE, 1, @sudOutputPinBitmap
- );
- TBCClassFactory.CreateFilter(TBCPushSourceBitmapSet, PushBitmapSetName,
- CLSID_PushSourceBitmapSet, CLSID_LegacyAmFilterCategory,
- MERIT_DO_NOT_USE, 1, @sudOutputPinBitmapSet
- );
- TBCClassFactory.CreateFilter(TBCPushSourceDesktop, PushDesktopName,
- CLSID_PushSourceDesktop, CLSID_LegacyAmFilterCategory,
- MERIT_DO_NOT_USE, 1, @sudOutputPinDesktop
- );
- end.