UPushSource.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:45k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. //------------------------------------------------------------------------------
  2. // File:              UPushSource.pas
  3. // Original file(s):  Setup.cpp, PushSource.h,
  4. //                    PushSourceBitmap.cpp, PushSourceBitmapSet.cpp,
  5. //                    PushSourceDesktop.cpp, PushSource.h,
  6. //                    DibHelper.cpp, DibHelper.h, PushGuids.h
  7. // Converted to Delphi by Nevhasymyy Andriy (E-Mail: a.n@email.com)
  8. // Thanks to Henri Gourvest (hgourvest@progdigy.com)
  9. //
  10. // Desc: DirectShow sample code - In-memory push mode source filter
  11. //
  12. // Portions created by Microsoft are
  13. // Copyright (c) 1992-2002 Microsoft Corporation.  All rights reserved.
  14. //------------------------------------------------------------------------------
  15. {
  16.   @abstract(In-memory push mode source filter Delphi conversion)
  17.   @author(Andriy Nevhasymyy: a.n@email.com)
  18.   @created(Jun 28, 2003)
  19.   @lastmod(Aug 04, 2003)
  20. }
  21. unit UPushSource;
  22. interface
  23. uses
  24.   BaseClass, DirectShow9, DSUtil,
  25.   Windows, ActiveX, SysUtils;
  26. const
  27.   //
  28.   // GUID definitions for PushSource filter set
  29.   //
  30.   CLSID_PushSourceBitmap: TGUID = '{E446D455-7C13-492A-9C96-38F948687E8A}';
  31.   CLSID_PushSourceBitmapSet: TGUID = '{D5070569-3C5F-4988-A77C-4DF44E850C43}';
  32.   CLSID_PushSourceDesktop: TGUID = '{570757C1-D2D8-42D1-BA0C-24E1BED3F62F}';
  33.   // Setup information
  34.   sudPinTypes: TRegPinTypes =
  35.   (
  36.     // video stream connection
  37.     clsMajorType: @MEDIATYPE_Video;
  38.     // all available
  39.     clsMinorType: @MEDIASUBTYPE_NULL
  40.     );
  41.   // pins info
  42.   sudOutputPinBitmap: array[0..0] of TRegFilterPins =
  43.   (
  44.     (
  45.     strName: 'Output'; // Pins string name
  46.     bRendered: FALSE; // Is it rendered
  47.     bOutput: TRUE; // Is it an output
  48.     bZero: FALSE; // Are we allowed none
  49.     bMany: FALSE; // And allowed many
  50.     oFilter: nil; // Connects to filter
  51.     strConnectsToPin: nil; // Connects to pin
  52.     nMediaTypes: 1; // Number of types
  53.     lpMediaType: @sudPinTypes // Pin information
  54.     )
  55.     );
  56.   sudOutputPinBitmapSet: array[0..0] of TRegFilterPins =
  57.   (
  58.     (
  59.     strName: 'Output'; // Pins string name
  60.     bRendered: FALSE; // Is it rendered
  61.     bOutput: TRUE; // Is it an output
  62.     bZero: FALSE; // Are we allowed none
  63.     bMany: FALSE; // And allowed many
  64.     oFilter: nil; // Connects to filter
  65.     strConnectsToPin: nil; // Connects to pin
  66.     nMediaTypes: 1; // Number of types
  67.     lpMediaType: @sudPinTypes // Pin information
  68.     )
  69.     );
  70.   sudOutputPinDesktop: array[0..0] of TRegFilterPins =
  71.   (
  72.     (
  73.     strName: 'Output'; // Pins string name
  74.     bRendered: FALSE; // Is it rendered
  75.     bOutput: TRUE; // Is it an output
  76.     bZero: FALSE; // Are we allowed none
  77.     bMany: FALSE; // And allowed many
  78.     oFilter: nil; // Connects to filter
  79.     strConnectsToPin: nil; // Connects to pin
  80.     nMediaTypes: 1; // Number of types
  81.     lpMediaType: @sudPinTypes // Pin information
  82.     )
  83.     );
  84.   UNITS = 10000000;
  85.   FPS_30 = UNITS div 30;
  86.   FPS_20 = UNITS div 20;
  87.   FPS_10 = UNITS div 10;
  88.   FPS_5 = UNITS div 5;
  89.   FPS_4 = UNITS div 4;
  90.   FPS_3 = UNITS div 3;
  91.   FPS_2 = UNITS div 2;
  92.   FPS_1 = UNITS div 1;
  93.   DefaultFrameLength: TReferenceTime = FPS_10;
  94.   // Filter name strings
  95.   PushBitmapName: WideString = '_ PushSource Bitmap Filter';
  96.   PushBitmapSetName: WideString = '_ PushSource BitmapSet Filter';
  97.   PushDesktopName: WideString = '_ PushSource Desktop Filter';
  98.   // default bitmap file name
  99.   BITMAP_NAME: string = 'sample.bmp';
  100.   // Number of bitmap files to load in the CPushPinBitmapSet class
  101.   Num_Files = 5;
  102. type
  103.   (**********************************************
  104.    *
  105.    *  Class declarations
  106.    *
  107.    **********************************************)
  108.   TBCPushPinBitmap = class(TBCSourceStream)
  109.   protected
  110.     // To track where we are in the file
  111.     FFramesWritten: Integer;
  112.     // Do we need to clear the buffer?
  113.     FZeroMemory: Boolean;
  114.     // The time stamp for each sample
  115.     FSampleTime: TRefTime;
  116.     // Pointer to the bitmap header
  117.     FBmi: PBitmapInfo;
  118.     // Size of the bitmap header
  119.     FBitmapInfo: DWord;
  120.     // File opening variables
  121.      // Handle returned from CreateFile
  122.     FFileHandle: THandle;
  123.     // Points to beginning of file buffer
  124.     FFileBuffer: PByte;
  125.     // Points to pixel bits
  126.     FImage: PByte;
  127.     // How many frames have been displayed
  128.     FFrameNumber: Integer;
  129.     // Duration of one frame
  130.     FFrameLength: TReferenceTime;
  131.     // Protects our internal state
  132.     FSharedState: TBCCritSec;
  133.   public
  134.     constructor Create(out hr: HResult; Filter: TBCSource);
  135.     destructor Destroy; override;
  136.     // Override the version that offers exactly one media type
  137.     function GetMediaType(MediaType: PAMMediaType): HResult; override;
  138.     function DecideBufferSize(Allocator: IMemAllocator;
  139.       Properties: PAllocatorProperties): HRESULT; override;
  140.     function FillBuffer(Sample: IMediaSample): HResult; override;
  141.     // Quality control
  142.    // Not implemented because we aren't going in real time.
  143.    // If the file-writing filter slows the graph down, we just do nothing, which means
  144.    // wait until we're unblocked. No frames are ever dropped.
  145.     // Quality control notifications sent to us
  146.     function Notify(Filter: IBaseFilter; q: TQuality): HRESULT; override;
  147.       stdcall;
  148.   end;
  149.   TBCPushPinBitmapSet = class(TBCSourceStream)
  150.   protected
  151.     // To track where we are in the file
  152.     FFramesWritten: Integer;
  153.     // Do we need to clear the buffer?
  154.     FZeroMemory: Boolean;
  155.     // The time stamp for each sample
  156.     FSampleTime: TRefTime;
  157.     // Array of bitmap headers pointers
  158.     FBmi: array[0..Num_Files - 1] of PBitmapInfo;
  159.     // Sizes of the bitmap headers
  160.     FBitmapInfo: array[0..Num_Files - 1] of DWord;
  161.     // File opening variables
  162.      // Handles returned from CreateFile
  163.     FFileHandle: array[0..Num_Files - 1] of THandle;
  164.     // Points to beginning of files buffer
  165.     FFileBuffer: array[0..Num_Files - 1] of PByte;
  166.     // Points to pixel bits
  167.     FImage: array[0..Num_Files - 1] of PByte;
  168.     // number of files loaded
  169.     FFilesLoaded: Boolean;
  170.     // Which bitmap is being displayed
  171.     FCurrentBitmap,
  172.       // How many frames have been displayed
  173.     FFrameNumber: Integer;
  174.     // Duration of one frame
  175.     FFrameLength: TReferenceTime;
  176.     // Protects our internal state
  177.     FSharedState: TBCCritSec;
  178.   public
  179.     constructor Create(out hr: HResult; Filter: TBCSource);
  180.     destructor Destroy; override;
  181.     // Override the version that offers exactly one media type
  182.     function GetMediaType(MediaType: PAMMediaType): HResult; override;
  183.     function DecideBufferSize(Allocator: IMemAllocator;
  184.       Properties: PAllocatorProperties): HRESULT; override;
  185.     function FillBuffer(Sample: IMediaSample): HResult; override;
  186.     // Quality control
  187.    // Not implemented because we aren't going in real time.
  188.    // If the file-writing filter slows the graph down, we just do nothing, which means
  189.    // wait until we're unblocked. No frames are ever dropped.
  190.     // Quality control notifications sent to us
  191.     function Notify(Filter: IBaseFilter; q: TQuality): HRESULT; override;
  192.       stdcall;
  193.   end;
  194.   TBCPushPinDesktop = class(TBCSourceStream)
  195.   protected
  196.     // To track where we are in the file
  197.     FFramesWritten: Integer;
  198.     // Do we need to clear the buffer?
  199.     FZeroMemory: Boolean;
  200.     // The time stamp for each sample
  201.     FSampleTime: TRefTime;
  202.     // How many frames have been displayed
  203.     FFrameNumber: Integer;
  204.     // Duration of one frame
  205.     FFrameLength: TReferenceTime;
  206.     // Rect containing entire screen coordinates
  207.     FScreenRect: TRect;
  208.     // The current image height
  209.     FImageHeight,
  210.       // And current image width
  211.     FImageWidth,
  212.       // Time in msec between frames
  213.     FRepeatTime,
  214.       // Screen bit depth
  215.     FCurrentBitDepth: Integer;
  216.     FMediaType: TAMMediaType;
  217.     // Protects our internal state
  218.     FSharedState: TBCCritSec;
  219.   public
  220.     constructor Create(out hr: HResult; Filter: TBCSource);
  221.     destructor Destroy; override;
  222.     // Override the version that offers exactly one media type
  223.     function GetMediaType(iPosition: Integer;
  224.       out MediaType: PAMMediaType): HResult; override;
  225.     // We will accept 8, 16, 24 or 32 bit video formats, in any
  226.     // image size that gives room to bounce.
  227.     // Returns E_INVALIDARG if the mediatype is not acceptable
  228.     function CheckMediaType(MediaType: PAMMediaType): HResult; override;
  229.     function DecideBufferSize(Allocator: IMemAllocator;
  230.       Properties: PAllocatorProperties): HRESULT; override;
  231.     function SetMediaType(MediaType: PAMMediaType): HRESULT; override;
  232.     function FillBuffer(Sample: IMediaSample): HResult; override;
  233.     // Quality control
  234.    // Not implemented because we aren't going in real time.
  235.    // If the file-writing filter slows the graph down, we just do nothing, which means
  236.    // wait until we're unblocked. No frames are ever dropped.
  237.     // Quality control notifications sent to us
  238.     function Notify(Filter: IBaseFilter; q: TQuality): HRESULT; override;
  239.       stdcall;
  240.   end;
  241.   // In-memory push mode source filter
  242.   // Provides a static bitmap as the video output stream.
  243.   TBCPushSourceBitmap = class(TBCSource)
  244.   private
  245.     FPin: TBCPushPinBitmap;
  246.   public
  247.     constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);
  248.     constructor CreateFromFactory(Factory: TBCClassFactory;
  249.       const Controller: IUnknown); override;
  250.     destructor Destroy; override;
  251.   end;
  252.   // In-memory push mode source filter
  253.   // Provides a rotating set of bitmaps as the video output stream.
  254.   TBCPushSourceBitmapset = class(TBCSource)
  255.   private
  256.     FPin: TBCPushPinBitmapSet;
  257.   public
  258.     constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);
  259.     constructor CreateFromFactory(Factory: TBCClassFactory;
  260.       const Controller: IUnknown); override;
  261.     destructor Destroy; override;
  262.   end;
  263.   // In-memory push mode source filter
  264.   // Provides an image of the user's desktop as a continuously updating stream.
  265.   TBCPushSourceDesktop = class(TBCSource)
  266.   private
  267.     FPin: TBCPushPinDesktop;
  268.   public
  269.     constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);
  270.     constructor CreateFromFactory(Factory: TBCClassFactory;
  271.       const Controller: IUnknown); override;
  272.     destructor Destroy; override;
  273.   end;
  274. implementation
  275. // --- TBCPushPinBitmap ---------------
  276. constructor TBCPushPinBitmap.Create(out hr: HResult; Filter: TBCSource);
  277. var
  278.   CurrentDir: array[0..MAX_PATH - 1] of Char;
  279.   CurrentFileName, MediaFileName: AnsiString;
  280.   MsgText: AnsiString;
  281.   FileSize, BytesRead: DWord;
  282.   FileHeaderSize: Integer;
  283.   BmpFileHeader: PBITMAPFILEHEADER;
  284.   pb: PByte;
  285. begin
  286.   inherited Create('_ Push Source Bitmap', hr, Filter, 'Out');
  287.   FFramesWritten := 0;
  288.   FZeroMemory := False;
  289.   FBmi := nil;
  290.   FBitmapInfo := 0;
  291.   FFileHandle := INVALID_HANDLE_VALUE;
  292.   FFileBuffer := nil;
  293.   FImage := nil;
  294.   FFrameNumber := 0;
  295.   // Display 5 bitmap frames per second
  296.   FFrameLength := FPS_5;
  297.   FSharedState := TBCCritSec.Create;
  298.   // The main point of this sample is to demonstrate how to take a DIB
  299.   // in host memory and insert it into a video stream.
  300.   // To keep this sample as simple as possible, we just read a single 24 bpp bitmap
  301.   // from a file and copy it into every frame that we send downstream.
  302.   // In the filter graph, we connect this filter to the AVI Mux, which creates
  303.   // the AVI file with the video frames we pass to it. In this case,
  304.   // the end result is a still image rendered as a video stream.
  305.   // Your filter will hopefully do something more interesting here.
  306.   // The main point is to set up a buffer containing the DIB pixel bits.
  307.   // This must be done before you start running.
  308.   // First look for the bitmap in the current directory
  309.   GetCurrentDirectory(MAX_PATH - 1, CurrentDir);
  310.   CurrentFileName := Format('%s%s', [CurrentDir, Bitmap_Name]);
  311.   FFileHandle := CreateFile(PChar(CurrentFileName), GENERIC_READ, 0, nil,
  312.     OPEN_EXISTING,
  313.     FILE_ATTRIBUTE_NORMAL, 0);
  314.   if (FFileHandle = INVALID_HANDLE_VALUE) then
  315.   begin
  316.     // File was not in the application's current directory,
  317.     // so look in the DirectX SDK media path instead.
  318.     MediaFileName := Format('%s%s'#0, [GetDXSDKMediaPath, Bitmap_Name]);
  319.     FFileHandle := CreateFile(PChar(MediaFileName), GENERIC_READ, 0, nil,
  320.       OPEN_EXISTING,
  321.       FILE_ATTRIBUTE_NORMAL, 0);
  322.     if (FFileHandle = INVALID_HANDLE_VALUE) then
  323.     begin
  324.       MsgText := Format('Could not open bitmap source file ' +
  325.         'in the application directory:'#13#10 + '%s'#13#10 +
  326.         'or in the DirectX SDK Media folder:'#13#10 + '%s'#13#10 +
  327.         'Please copy this file either to the application''s folder'#13#10 +
  328.         'or to the DirectX SDK Media folder, then recreate this filter'#13#10 +
  329.         'Otherwise, you will not be able to render the output pin',
  330.         [CurrentFileName, MediaFileName]);
  331.       OutputDebugString(PChar(MsgText));
  332.       MessageBox(0, PChar(MsgText), 'PushSource filter error',
  333.         MB_ICONERROR or MB_OK);
  334.       hr := HRESULTFROMWIN32(GetLastError);
  335.       Exit;
  336.     end;
  337.   end;
  338.   FileSize := GetFileSize(FFileHandle, nil);
  339.   if (FileSize = INVALID_FILE_SIZE) then
  340.   begin
  341. {$IFDEF DEBUG}
  342.     DbgLog(Self, 'Invalid file size');
  343. {$ENDIF}
  344.     hr := HRESULTFROMWIN32(GetLastError());
  345.     Exit;
  346.   end;
  347.   FFileBuffer := CoTaskMemAlloc(FileSize);
  348.   if (FFileBuffer = nil) then
  349.   begin
  350.     OutputDebugString('Could not allocate FImage');
  351.     hr := E_OUTOFMEMORY;
  352.     Exit;
  353.   end;
  354.   BytesRead := 0;
  355.   if not (ReadFile(FFileHandle, FFileBuffer^, FileSize, BytesRead, nil)) then
  356.   begin
  357.     hr := HRESULTFROMWIN32(GetLastError());
  358.     OutputDebugString('ReadFile failed');
  359.     Exit;
  360.   end;
  361.   // WARNING - This code does not verify that the file is a valid bitmap file.
  362.   // In your own filter, you would check this or else generate the bitmaps
  363.   // yourself in memory.
  364.   FileHeaderSize := SizeOf(BITMAPFILEHEADER);
  365.   // Store the size of the BITMAPINFO
  366.   BmpFileHeader := PBITMAPFILEHEADER(FFileBuffer);
  367.   FBitmapInfo := Integer(BmpFileHeader.bfOffBits) - FileHeaderSize;
  368.   // Store a pointer to the BITMAPINFO
  369.   pb := PByte(FFileBuffer);
  370.   Inc(pb, FileHeaderSize);
  371.   FBmi := PBITMAPINFO(pb);
  372.   // Store a pointer to the starting address of the pixel bits
  373.   Inc(pb, FBitmapInfo);
  374.   FImage := pb;
  375.   // Close and invalidate the file handle, since we have copied its bitmap data
  376.   CloseHandle(FFileHandle);
  377.   FFileHandle := INVALID_HANDLE_VALUE;
  378. end;
  379. destructor TBCPushPinBitmap.Destroy;
  380. begin
  381. {$IFDEF DEBUG}
  382.   DbgLog(self, Format('Frames written %d', [FFrameNumber]));
  383. {$ENDIF}
  384.   if Assigned(FFileBuffer) then
  385.   begin
  386.     //FreeMem(FFileBuffer);
  387.     CoTaskMemFree(FFileBuffer);
  388.     FFileBuffer := nil;
  389.   end;
  390.   // The constructor might quit early on error and not close the file...
  391.   if (FFileHandle <> INVALID_HANDLE_VALUE) then
  392.     CloseHandle(FFileHandle);
  393.   if (FSharedState <> nil) then
  394.     FreeAndNil(FSharedState);
  395.   inherited;
  396. end;
  397. // GetMediaType: This method tells the downstream pin what types we support.
  398. // Here is how CSourceStream deals with media types:
  399. //
  400. // If you support exactly one type, override GetMediaType(MediaType : PAMMediaType).
  401. // It will then be called when (a) our filter proposes a media type,
  402. // (b) the other filter proposes a type and we have to check that type.
  403. //
  404. // If you support > 1 type, override GetMediaType(iPosition : Integer;
  405. //  out MediaType : PAMMediaType) AND CheckMediaType.
  406. //
  407. // In this case we support only one type, which we obtain from the bitmap file.
  408. function TBCPushPinBitmap.GetMediaType(MediaType: PAMMediaType): HResult;
  409. var
  410.   pvi: PVIDEOINFOHEADER;
  411. begin
  412.   FFilter.StateLock.Lock;
  413.   try
  414.     if (MediaType = nil) then
  415.     begin
  416.       Result := E_POINTER;
  417.       Exit;
  418.     end;
  419.     // If the bitmap file was not loaded, just fail here.
  420.     if (FImage = nil) then
  421.     begin
  422.       Result := E_FAIL;
  423.       Exit;
  424.     end;
  425.     // Allocate enough room for the VIDEOINFOHEADER and the color tables
  426.     MediaType.cbFormat := SIZE_PREHEADER + FBitmapInfo;
  427.     pvi := CoTaskMemAlloc(MediaType.cbFormat);
  428.     if (pvi = nil) then
  429.     begin
  430.       Result := E_OUTOFMEMORY;
  431.       Exit;
  432.     end;
  433.     ZeroMemory(pvi, MediaType.cbFormat);
  434.     pvi.AvgTimePerFrame := FFrameLength;
  435.     // Copy the header info
  436.     CopyMemory(@pvi.bmiHeader, FBmi, FBitmapInfo);
  437.     // Set image size for use in FillBuffer
  438.     pvi.bmiHeader.biSizeImage := GetBitmapSize(@pvi.bmiHeader);
  439.     // Clear source and target rectangles
  440.     // we want the whole image area rendered
  441.     SetRectEmpty(pvi.rcSource);
  442.     // no particular destination rectangle
  443.     SetRectEmpty(pvi.rcTarget);
  444.     MediaType.majortype := MEDIATYPE_Video;
  445.     MediaType.formattype := FORMAT_VideoInfo;
  446.     // Work out the GUID for the subtype from the header info.
  447.     MediaType.subtype := GetBitmapSubtype(@pvi.bmiHeader);
  448.     MediaType.bTemporalCompression := False;
  449.     MediaType.bFixedSizeSamples := True;
  450.     MediaType.pbFormat := pvi;
  451.     MediaType.lSampleSize := pvi.bmiHeader.biSizeImage;
  452.     Result := S_OK;
  453.   finally
  454.     FFilter.StateLock.UnLock;
  455.   end;
  456. end;
  457. function TBCPushPinBitmap.DecideBufferSize(Allocator: IMemAllocator;
  458.   Properties: PAllocatorProperties): HRESULT;
  459. var
  460.   pvi: PVIDEOINFOHEADER;
  461.   Actual: ALLOCATOR_PROPERTIES;
  462. begin
  463.   if (Allocator = nil) or (Properties = nil) then
  464.   begin
  465.     Result := E_POINTER;
  466.     Exit;
  467.   end;
  468.   FFilter.StateLock.Lock;
  469.   try
  470.     // If the bitmap file was not loaded, just fail here.
  471.     if (FImage = nil) then
  472.     begin
  473.       Result := E_FAIL;
  474.       Exit;
  475.     end;
  476.     pvi := AMMediaType.pbFormat;
  477.     // Ensure a minimum number of buffers
  478.     if (Properties.cBuffers = 0) then
  479.       Properties.cBuffers := 2;
  480.     Properties.cbBuffer := pvi.bmiHeader.biSizeImage;
  481.     Result := Allocator.SetProperties(Properties^, Actual);
  482.     if Failed(Result) then
  483.       Exit;
  484.     // Is this allocator unsuitable?
  485.     if (Actual.cbBuffer < Properties.cbBuffer) then
  486.       Result := E_FAIL
  487.     else
  488.       Result := S_OK;
  489.   finally
  490.     FFilter.StateLock.UnLock;
  491.   end;
  492. end;
  493. // This is where we insert the DIB bits into the video stream.
  494. // FillBuffer is called once for every sample in the stream.
  495. function TBCPushPinBitmap.FillBuffer(Sample: IMediaSample): HResult;
  496. var
  497.   pData: PByte;
  498.   cbData: Longint;
  499.   pvi: PVIDEOINFOHEADER;
  500.   Start, Stop: REFERENCE_TIME;
  501.   function min(v1, v2: DWord): DWord;
  502.   begin
  503.     if v1 <= v2 then
  504.       Result := v1
  505.     else
  506.       Result := v2;
  507.   end;
  508. begin
  509.   if (Sample = nil) then
  510.   begin
  511.     Result := E_POINTER;
  512.     Exit;
  513.   end;
  514.   // If the bitmap file was not loaded, just fail here.
  515.   if (FImage = nil) then
  516.   begin
  517.     Result := E_FAIL;
  518.     Exit;
  519.   end;
  520.   FSharedState.Lock;
  521.   try
  522.     // Access the sample's data buffer
  523.     Sample.GetPointer(pData);
  524.     cbData := Sample.GetSize;
  525.     // Check that we're still using video
  526.     Assert(IsEqualGUID(AMMediaType.formattype, FORMAT_VideoInfo));
  527.     pvi := AMMediaType.pbFormat;
  528.     // If we want to change the contents of our source buffer (FImage)
  529.     // at some interval or based on some condition, this is where to do it.
  530.     // Remember that the new data has the same format that we specified in GetMediaType.
  531.     // For example:
  532.     // if(FFrameNumber > SomeValue)
  533.     //    LoadNewBitsIntoBuffer(FImage)
  534.     // Copy the DIB bits over into our filter's output buffer.
  535.     // Since sample size may be larger than the image size, bound the copy size.
  536.     CopyMemory(pData, FImage, min(pvi.bmiHeader.biSizeImage, cbData));
  537.     // Set the timestamps that will govern playback frame rate.
  538.     // If this file is getting written out as an AVI,
  539.     // then you'll also need to configure the AVI Mux filter to
  540.     // set the Average Time Per Frame for the AVI Header.
  541.     // The current time is the sample's start
  542.     Start := FFrameNumber * FFrameLength;
  543.     Stop := Start + FFrameLength;
  544.     Sample.SetTime(@Start, @Stop);
  545.     Inc(FFrameNumber);
  546.     // Set TRUE on every sample for uncompressed frames
  547.     Sample.SetSyncPoint(True);
  548.     Result := S_OK;
  549.   finally
  550.     FSharedState.UnLock;
  551.   end;
  552. end;
  553. function TBCPushPinBitmap.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;
  554. begin
  555.   Result := E_FAIL;
  556. end;
  557. // --- TBCPushSourceBitmap ------------
  558. constructor TBCPushSourceBitmap.Create(ObjName: string; Unk: IUnKnown;
  559.   out hr: HRESULT);
  560. begin
  561.   inherited Create(ObjName, Unk, CLSID_PushSourceBitmap);
  562.   // The pin magically adds itself to our pin array.
  563.   FPin := TBCPushPinBitmap.Create(hr, Self);
  564.   if (hr <> S_OK) then
  565.     if (FPin = nil) then
  566.       hr := E_OUTOFMEMORY;
  567. end;
  568. constructor TBCPushSourceBitmap.CreateFromFactory(Factory: TBCClassFactory;
  569.   const Controller: IUnknown);
  570. var
  571.   hr: HRESULT;
  572. begin
  573.   Create(Factory.Name, Controller, hr);
  574. end;
  575. destructor TBCPushSourceBitmap.Destroy;
  576. begin
  577.   FreeAndNil(FPin);
  578.   inherited;
  579. end;
  580. // --- TBCPushPinBitmapSet ------------
  581. constructor TBCPushPinBitmapSet.Create(out hr: HResult; Filter: TBCSource);
  582. var
  583.   CurrentDir: array[0..MAX_PATH - 1] of Char;
  584.   CurrentFileName, MediaFileName: AnsiString;
  585.   MsgText: AnsiString;
  586.   FileSize, BytesRead: DWord;
  587.   FilesLoaded, FileHeaderSize: Integer;
  588.   BmpFileHeader: PBITMAPFILEHEADER;
  589.   pb: PByte;
  590.   i: Integer;
  591. begin
  592.   inherited Create('_ Push Source Bitmap Set', hr, Filter, 'Out');
  593.   FFramesWritten := 0;
  594.   FZeroMemory := False;
  595.   FFrameNumber := 0;
  596.   // Display 5 bitmap frames per second
  597.   FFrameLength := FPS_2;
  598.   FSharedState := TBCCritSec.Create;
  599.   FCurrentBitmap := 0;
  600.   FFilesLoaded := False;
  601.   FilesLoaded := 0;
  602.   // Initialize member data arrays
  603.   ZeroMemory(@FBitmapInfo, NUM_FILES * sizeof(DWord));
  604.   ZeroMemory(@FBmi, NUM_FILES * sizeof(PBitmapInfo));
  605.   ZeroMemory(@FFileHandle, NUM_FILES * sizeof(THandle));
  606.   ZeroMemory(@FFileBuffer, NUM_FILES * sizeof(Byte));
  607.   ZeroMemory(@FImage, NUM_FILES * sizeof(Byte));
  608.   // The main point of this sample is to demonstrate how to take a DIB
  609.   // in host memory and insert it into a video stream.
  610.   // We read a set of bitmaps from files and copy one bitmap
  611.   // into every frame that we send downstream.
  612.   // In the filter graph, we connect this filter to the AVI Mux, which creates
  613.   // the AVI file with the video frames we pass to it. In this case,
  614.   // the end result is a rotating set of images rendered as a video stream.
  615.   // First look for the bitmap in the current directory
  616.   GetCurrentDirectory(MAX_PATH - 1, CurrentDir);
  617.   for i := 0 to NUM_FILES - 1 do
  618.   begin
  619.     // Assume that the bitmap in the application's directory
  620.     CurrentFileName := Format('%sBitmapSet%d.bmp', [CurrentDir, i]);
  621.     FFileHandle[i] := CreateFile(PChar(CurrentFileName), GENERIC_READ, 0, nil,
  622.       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  623.     if (FFileHandle[i] = INVALID_HANDLE_VALUE) then
  624.     begin
  625.       // File was not in the application's current directory,
  626.       // so look in the DirectX SDK media path instead.  The path contained
  627.       // in szMediaDir will already have a trailing backslash ''.
  628.       MediaFileName := Format('%sBitmapSet%d.bmp', [GetDXSDKMediaPath, i]);
  629.       FFileHandle[i] := CreateFile(PChar(MediaFileName), GENERIC_READ, 0, nil,
  630.         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  631.       if (FFileHandle[i] = INVALID_HANDLE_VALUE) then
  632.       begin
  633.         MsgText := Format('Could not open bitmap source file (#%d of %d) ' +
  634.           'in the application directory:'#13#10 + '%s'#13#10 +
  635.           'or in the DirectX SDK Media folder:'#13#10 + '%s'#13#10 +
  636.           'Please copy this file either to the application''s folder'#13#10 +
  637.           'or to the DirectX SDK Media folder, then recreate this filter'#13#10 +
  638.           'Otherwise, you will not be able to render the output pin',
  639.           [i + 1, NUM_FILES, CurrentFileName, MediaFileName]);
  640.         OutputDebugString(PChar(MsgText));
  641.         MessageBox(0, PChar(MsgText), 'PushSource filter error',
  642.           MB_ICONERROR or MB_OK);
  643.         hr := HRESULTFROMWIN32(GetLastError());
  644.         Exit;
  645.       end;
  646.     end;
  647.     FileSize := GetFileSize(FFileHandle[i], nil);
  648.     if (FileSize = INVALID_FILE_SIZE) then
  649.     begin
  650. {$IFDEF DEBUG}
  651.       DbgLog(Self, 'Invalid file size');
  652. {$ENDIF}
  653.       hr := HRESULTFROMWIN32(GetLastError());
  654.       Exit;
  655.     end;
  656.     FFileBuffer[i] := CoTaskMemAlloc(FileSize);
  657.     if (FFileBuffer[i] = nil) then
  658.     begin
  659.       OutputDebugString('Could not allocate FImage');
  660.       hr := E_OUTOFMEMORY;
  661.       Exit;
  662.     end;
  663.     BytesRead := 0;
  664.     if not (ReadFile(FFileHandle[i], FFileBuffer[i]^, FileSize, BytesRead, nil))
  665.       then
  666.     begin
  667.       hr := HRESULTFROMWIN32(GetLastError());
  668.       OutputDebugString('ReadFile failed');
  669.       Exit;
  670.     end;
  671.     // WARNING - This code does not verify that the file is a valid bitmap file.
  672.     // In your own filter, you would check this or else generate the bitmaps
  673.     // yourself in memory.
  674.     FileHeaderSize := SizeOf(BITMAPFILEHEADER);
  675.     // Store the size of the BITMAPINFO
  676.     BmpFileHeader := PBITMAPFILEHEADER(FFileBuffer[i]);
  677.     FBitmapInfo[i] := Integer(BmpFileHeader.bfOffBits) - FileHeaderSize;
  678.     // Store a pointer to the BITMAPINFO
  679.     pb := PByte(FFileBuffer[i]);
  680.     Inc(pb, FileHeaderSize);
  681.     FBmi[i] := PBITMAPINFO(pb);
  682.     // Store a pointer to the starting address of the pixel bits
  683.     Inc(pb, FBitmapInfo[i]);
  684.     FImage[i] := pb;
  685.     // Close and invalidate the file handle, since we have copied its bitmap data
  686.     CloseHandle(FFileHandle[i]);
  687.     FFileHandle[i] := INVALID_HANDLE_VALUE;
  688.     // Count this is a successful file load.  If not all files load
  689.     // properly, then the filter will not operate correctly.
  690.     Inc(FilesLoaded);
  691.   end;
  692.   // Make sure that ALL files were properly loaded
  693.   if (FilesLoaded <> NUM_FILES) then
  694.     hr := E_FAIL
  695.   else
  696.   begin
  697.     FFilesLoaded := TRUE;
  698.   end;
  699. end;
  700. destructor TBCPushPinBitmapSet.Destroy;
  701. var
  702.   i: Integer;
  703. begin
  704. {$IFDEF DEBUG}
  705.   DbgLog(self, Format('Frames written %d', [FFrameNumber]));
  706. {$ENDIF}
  707.   for i := 0 to NUM_FILES - 1 do
  708.   begin
  709.     if Assigned(FFileBuffer[i]) then
  710.     begin
  711.       CoTaskMemFree(FFileBuffer[i]);
  712.       FFileBuffer[i] := nil;
  713.     end;
  714.     // The constructor might quit early on error and not close the file...
  715.     if (FFileHandle[i] <> INVALID_HANDLE_VALUE) then
  716.       CloseHandle(FFileHandle[i]);
  717.   end;
  718.   if (FSharedState <> nil) then
  719.     FreeAndNil(FSharedState);
  720.   inherited;
  721. end;
  722. // GetMediaType: This method tells the downstream pin what types we support.
  723. // Here is how CSourceStream deals with media types:
  724. //
  725. // If you support exactly one type, override GetMediaType(MediaType : PAMMediaType).
  726. // It will then be called when (a) our filter proposes a media type,
  727. // (b) the other filter proposes a type and we have to check that type.
  728. //
  729. // If you support > 1 type, override GetMediaType(iPosition : Integer;
  730. //  out MediaType : PAMMediaType) AND CheckMediaType.
  731. //
  732. // In this case we support only one type, which we obtain from the bitmap file.
  733. function TBCPushPinBitmapSet.GetMediaType(MediaType: PAMMediaType): HResult;
  734. var
  735.   pvi: PVIDEOINFOHEADER;
  736. begin
  737.   FFilter.StateLock.Lock;
  738.   try
  739.     if (MediaType = nil) then
  740.     begin
  741.       Result := E_POINTER;
  742.       Exit;
  743.     end;
  744.     // If the bitmap files were not loaded, just fail here.
  745.     if not (FFilesLoaded) then
  746.     begin
  747.       Result := E_FAIL;
  748.       Exit;
  749.     end;
  750.     // Allocate enough room for the VIDEOINFOHEADER and the color tables
  751.     MediaType.cbFormat := SIZE_PREHEADER + FBitmapInfo[FCurrentBitmap];
  752.     pvi := CoTaskMemAlloc(MediaType.cbFormat);
  753.     if (pvi = nil) then
  754.     begin
  755.       Result := E_OUTOFMEMORY;
  756.       Exit;
  757.     end;
  758.     ZeroMemory(pvi, MediaType.cbFormat);
  759.     pvi.AvgTimePerFrame := FFrameLength;
  760.     // Copy the header info
  761.     CopyMemory(@pvi.bmiHeader, FBmi[FCurrentBitmap],
  762.       FBitmapInfo[FCurrentBitmap]);
  763.     // Set image size for use in FillBuffer
  764.     pvi.bmiHeader.biSizeImage := GetBitmapSize(@pvi.bmiHeader);
  765.     // Clear source and target rectangles
  766.     // we want the whole image area rendered
  767.     SetRectEmpty(pvi.rcSource);
  768.     // no particular destination rectangle
  769.     SetRectEmpty(pvi.rcTarget);
  770.     MediaType.majortype := MEDIATYPE_Video;
  771.     MediaType.formattype := FORMAT_VideoInfo;
  772.     // Work out the GUID for the subtype from the header info.
  773.     MediaType.subtype := GetBitmapSubtype(@pvi.bmiHeader);
  774.     MediaType.bTemporalCompression := False;
  775.     MediaType.bFixedSizeSamples := True;
  776.     //MediaType.cbFormat := SizeOf(TBitmapInfo);
  777.     MediaType.pbFormat := pvi;
  778.     MediaType.lSampleSize := pvi.bmiHeader.biSizeImage;
  779.     Result := S_OK;
  780.   finally
  781.     FFilter.StateLock.UnLock;
  782.   end;
  783. end;
  784. function TBCPushPinBitmapSet.DecideBufferSize(Allocator: IMemAllocator;
  785.   Properties: PAllocatorProperties): HRESULT;
  786. var
  787.   pvi: PVIDEOINFOHEADER;
  788.   Actual: ALLOCATOR_PROPERTIES;
  789. begin
  790.   if (Allocator = nil) or (Properties = nil) then
  791.   begin
  792.     Result := E_POINTER;
  793.     Exit;
  794.   end;
  795.   FFilter.StateLock.Lock;
  796.   try
  797.     // If the bitmap files were not loaded, just fail here.
  798.     if not (FFilesLoaded) then
  799.     begin
  800.       Result := E_FAIL;
  801.       Exit;
  802.     end;
  803.     pvi := AMMediaType.pbFormat;
  804.     // Ensure a minimum number of buffers
  805.     if (Properties.cBuffers = 0) then
  806.       Properties.cBuffers := 2;
  807.     Properties.cbBuffer := pvi.bmiHeader.biSizeImage;
  808.     Result := Allocator.SetProperties(Properties^, Actual);
  809.     if Failed(Result) then
  810.       Exit;
  811.     // Is this allocator unsuitable?
  812.     if (Actual.cbBuffer < Properties.cbBuffer) then
  813.       Result := E_FAIL
  814.     else
  815.       Result := S_OK;
  816.   finally
  817.     FFilter.StateLock.UnLock;
  818.   end;
  819. end;
  820. // This is where we insert the DIB bits into the video stream.
  821. // FillBuffer is called once for every sample in the stream.
  822. function TBCPushPinBitmapSet.FillBuffer(Sample: IMediaSample): HResult;
  823. var
  824.   pData: PByte;
  825.   cbData: Longint;
  826.   pvi: PVIDEOINFOHEADER;
  827.   Start, Stop: REFERENCE_TIME;
  828.   function min(v1, v2: DWord): DWord;
  829.   begin
  830.     if v1 <= v2 then
  831.       Result := v1
  832.     else
  833.       Result := v2;
  834.   end;
  835. begin
  836.   // If the bitmap files were not loaded, just fail here.
  837.   if not (FFilesLoaded) then
  838.   begin
  839.     Result := E_FAIL;
  840.     Exit;
  841.   end;
  842.   if (Sample = nil) then
  843.   begin
  844.     Result := E_POINTER;
  845.     Exit;
  846.   end;
  847.   FSharedState.Lock;
  848.   try
  849.     // Access the sample's data buffer
  850.     Sample.GetPointer(pData);
  851.     cbData := Sample.GetSize;
  852.     // Check that we're still using video
  853.     Assert(IsEqualGUID(AMMediaType.formattype, FORMAT_VideoInfo));
  854.     pvi := AMMediaType.pbFormat;
  855.     // Copy the DIB bits over into our filter's output buffer.
  856.     // Since sample size may be larger than the image size, bound the copy size.
  857.     // Remember that the new data has the same format
  858.     // that we specified in GetMediaType.
  859.     CopyMemory(pData, FImage[FCurrentBitmap],
  860.       min(pvi.bmiHeader.biSizeImage, cbData));
  861.     // Set the timestamps that will govern playback frame rate.
  862.     // If this file is getting written out as an AVI,
  863.     // then you'll also need to configure the AVI Mux filter to
  864.     // set the Average Time Per Frame for the AVI Header.
  865.     // The current time is the sample's start
  866.     Start := FFrameNumber * FFrameLength;
  867.     Stop := Start + FFrameLength;
  868.     Sample.SetTime(@Start, @Stop);
  869.     Inc(FFrameNumber);
  870.     // Set TRUE on every sample for uncompressed frames
  871.     Sample.SetSyncPoint(True);
  872.     // Increment the current buffer so that the next FillBuffer() call
  873.     // will use the bits from the next bitmap in the set.
  874.     Inc(FCurrentBitmap);
  875.     FCurrentBitmap := FCurrentBitmap mod NUM_FILES;
  876.     Result := S_OK;
  877.   finally
  878.     FSharedState.UnLock;
  879.   end;
  880. end;
  881. function TBCPushPinBitmapSet.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;
  882. begin
  883.   Result := E_FAIL;
  884. end;
  885. (**********************************************
  886.  *
  887.  *  CPushSourceBitmapSet Class
  888.  *
  889.  **********************************************)
  890. constructor TBCPushSourceBitmapSet.Create(ObjName: string; Unk: IUnKnown;
  891.   out hr: HRESULT);
  892. begin
  893.   inherited Create(ObjName, Unk, CLSID_PushSourceBitmapSet);
  894.   // The pin magically adds itself to our pin array.
  895.   FPin := TBCPushPinBitmapSet.Create(hr, Self);
  896.   if (hr <> S_OK) then
  897.     if (FPin = nil) then
  898.       hr := E_OUTOFMEMORY;
  899. end;
  900. constructor TBCPushSourceBitmapSet.CreateFromFactory(Factory: TBCClassFactory;
  901.   const Controller: IUnknown);
  902. var
  903.   hr: HRESULT;
  904. begin
  905.   Create(Factory.Name, Controller, hr);
  906. end;
  907. destructor TBCPushSourceBitmapSet.Destroy;
  908. begin
  909.   FreeAndNil(FPin);
  910.   inherited;
  911. end;
  912. // --- TBCPushPinDesktop --------------
  913. constructor TBCPushPinDesktop.Create(out hr: HResult; Filter: TBCSource);
  914. var
  915.   DC: HDC;
  916. begin
  917.   inherited Create('_ Push Source Desktop', hr, Filter, 'Out');
  918.   FFramesWritten := 0;
  919.   FZeroMemory := False;
  920.   FFrameNumber := 0;
  921.   // Display 5 bitmap frames per second
  922.   FFrameLength := FPS_5;
  923.   FSharedState := TBCCritSec.Create;
  924.   FCurrentBitDepth := 32;
  925.   // The main point of this sample is to demonstrate how to take a DIB
  926.   // in host memory and insert it into a video stream.
  927.   // To keep this sample as simple as possible, we just read the desktop image
  928.   // from a file and copy it into every frame that we send downstream.
  929.   // In the filter graph, we connect this filter to the AVI Mux, which creates
  930.    // the AVI file with the video frames we pass to it. In this case,
  931.    // the end result is a screen capture video (GDI images only, with no
  932.    // support for overlay surfaces).
  933.    // Get the device context of the main display
  934.   DC := CreateDC('DISPLAY', nil, nil, nil);
  935.   // Get the dimensions of the main desktop window
  936.   FScreenRect.Left := 0;
  937.   FScreenRect.Top := 0;
  938.   FScreenRect.Right := GetDeviceCaps(DC, HORZRES);
  939.   FScreenRect.Bottom := GetDeviceCaps(DC, VERTRES);
  940.   // Save dimensions for later use in FillBuffer()
  941.   FImageWidth := FScreenRect.Right - FScreenRect.Left;
  942.   FImageHeight := FScreenRect.Bottom - FScreenRect.Top;
  943.   // Release the device context
  944.   DeleteDC(DC);
  945.   hr := S_OK;
  946. end;
  947. destructor TBCPushPinDesktop.Destroy;
  948. begin
  949. {$IFDEF DEBUG}
  950.   DbgLog(self, Format('Frames written %d', [FFrameNumber]));
  951. {$ENDIF}
  952.   inherited;
  953. end;
  954. // GetMediaType
  955. //
  956. // Prefer 5 formats - 8, 16 (*2), 24 or 32 bits per pixel
  957. //
  958. // Prefered types should be ordered by quality, with zero as highest quality.
  959. // Therefore, iPosition =
  960. //      0    Return a 32bit mediatype
  961. //      1    Return a 24bit mediatype
  962. //      2    Return 16bit RGB565
  963. //      3    Return a 16bit mediatype (rgb555)
  964. //      4    Return 8 bit palettised format
  965. //      >4   Invalid
  966. //
  967. function TBCPushPinDesktop.GetMediaType(iPosition: Integer;
  968.   out MediaType: PAMMediaType): HResult;
  969. var
  970.   pvi: PVIDEOINFO;
  971.   i: Integer;
  972. begin
  973.   FFilter.StateLock.Lock;
  974.   try
  975.     if (MediaType = nil) then
  976.     begin
  977.       Result := E_POINTER;
  978.       Exit;
  979.     end;
  980.     if (iPosition < 0) then
  981.     begin
  982.       Result := E_INVALIDARG;
  983.       Exit;
  984.     end;
  985.     // Have we run off the end of types?
  986.     if (iPosition > 4) then
  987.     begin
  988.       Result := VFW_S_NO_MORE_ITEMS;
  989.       Exit;
  990.     end;
  991.     MediaType.cbFormat := SizeOf(TVideoInfo);
  992.     pvi := CoTaskMemAlloc(MediaType.cbFormat);
  993.     if (pvi = nil) then
  994.     begin
  995.       Result := E_OUTOFMEMORY;
  996.       Exit;
  997.     end;
  998.     // Initialize the VideoInfo structure before configuring its members
  999.     ZeroMemory(pvi, MediaType.cbFormat);
  1000.     case iPosition of
  1001.       0:
  1002.         begin
  1003.           // Return our highest quality 32bit format
  1004.           // Since we use RGB888 (the default for 32 bit), there is
  1005.           // no reason to use BI_BITFIELDS to specify the RGB
  1006.           // masks. Also, not everything supports BI_BITFIELDS
  1007.           pvi.bmiHeader.biCompression := BI_RGB;
  1008.           pvi.bmiHeader.biBitCount := 32;
  1009.         end;
  1010.       1:
  1011.         begin
  1012.           // Return our 24bit format
  1013.           pvi.bmiHeader.biCompression := BI_RGB;
  1014.           pvi.bmiHeader.biBitCount := 24;
  1015.         end;
  1016.       2:
  1017.         begin
  1018.           // 16 bit per pixel RGB565
  1019.           // Place the RGB masks as the first 3 doublewords in the palette area
  1020.           for i := 0 to 2 do
  1021.             pvi.TrueColorInfo.dwBitMasks[i] := bits565[i];
  1022.           pvi.bmiHeader.biCompression := BI_BITFIELDS;
  1023.           pvi.bmiHeader.biBitCount := 16;
  1024.         end;
  1025.       3:
  1026.         begin
  1027.           // 16 bits per pixel RGB555
  1028.           // Place the RGB masks as the first 3 doublewords in the palette area
  1029.           for i := 0 to 2 do
  1030.             pvi.TrueColorInfo.dwBitMasks[i] := bits555[i];
  1031.           pvi.bmiHeader.biCompression := BI_BITFIELDS;
  1032.           pvi.bmiHeader.biBitCount := 16;
  1033.         end;
  1034.       4:
  1035.         begin
  1036.           // 8 bit palettised
  1037.           pvi.bmiHeader.biCompression := BI_RGB;
  1038.           pvi.bmiHeader.biBitCount := 8;
  1039.           pvi.bmiHeader.biClrUsed := iPALETTE_COLORS;
  1040.         end;
  1041.     end;
  1042.     // Adjust the parameters common to all formats
  1043.     pvi.bmiHeader.biSize := SizeOf(TBitmapInfoHeader);
  1044.     pvi.bmiHeader.biWidth := FImageWidth;
  1045.     pvi.bmiHeader.biHeight := FImageHeight;
  1046.     pvi.bmiHeader.biPlanes := 1;
  1047.     pvi.bmiHeader.biSizeImage := GetBitmapSize(@pvi.bmiHeader);
  1048.     pvi.bmiHeader.biClrImportant := 0;
  1049.     // Clear source and target rectangles
  1050.     // we want the whole image area rendered
  1051.     SetRectEmpty(pvi.rcSource);
  1052.     // no particular destination rectangle
  1053.     SetRectEmpty(pvi.rcTarget);
  1054.     MediaType.majortype := MEDIATYPE_Video;
  1055.     MediaType.formattype := FORMAT_VideoInfo;
  1056.     MediaType.bTemporalCompression := False;
  1057.     MediaType.bFixedSizeSamples := True;
  1058.     // Work out the GUID for the subtype from the header info.
  1059.     MediaType.subtype := GetBitmapSubtype(@pvi.bmiHeader);
  1060.     MediaType.pbFormat := pvi;
  1061.     MediaType.lSampleSize := pvi.bmiHeader.biSizeImage;
  1062.     Result := S_OK;
  1063.   finally
  1064.     FFilter.StateLock.UnLock;
  1065.   end;
  1066. end;
  1067. // We will accept 8, 16, 24 or 32 bit video formats, in any
  1068. // image size that gives room to bounce.
  1069. // Returns E_INVALIDARG if the mediatype is not acceptable
  1070. function TBCPushPinDesktop.CheckMediaType(MediaType: PAMMediaType): HResult;
  1071. var
  1072.   pvi: PVIDEOINFO;
  1073.   SubType: TGUID;
  1074. begin
  1075.   // we only output video
  1076.   if not (IsEqualGUID(MediaType.majortype, MEDIATYPE_Video)) or
  1077.     // in fixed size samples
  1078.   not (MediaType.bFixedSizeSamples) then
  1079.   begin
  1080.     Result := E_INVALIDARG;
  1081.     Exit;
  1082.   end;
  1083.   // Check for the subtypes we support
  1084.   SubType := MediaType.subtype;
  1085.   if IsEqualGUID(SubType, GUID_NULL) then
  1086.   begin
  1087.     Result := E_INVALIDARG;
  1088.     Exit;
  1089.   end;
  1090.   if not (
  1091.     IsEqualGUID(SubType, MEDIASUBTYPE_RGB8) or
  1092.     IsEqualGUID(SubType, MEDIASUBTYPE_RGB565) or
  1093.     IsEqualGUID(SubType, MEDIASUBTYPE_RGB555) or
  1094.     IsEqualGUID(SubType, MEDIASUBTYPE_RGB24) or
  1095.     IsEqualGUID(SubType, MEDIASUBTYPE_RGB32)
  1096.     ) then
  1097.   begin
  1098.     Result := E_INVALIDARG;
  1099.     Exit;
  1100.   end;
  1101.   // Get the format area of the media type
  1102.   pvi := MediaType.pbFormat;
  1103.   if (pvi = nil) then
  1104.   begin
  1105.     Result := E_INVALIDARG;
  1106.     Exit;
  1107.   end;
  1108.   // Check if the image width & height have changed
  1109.   if (pvi.bmiHeader.biWidth <> FImageWidth) or
  1110.     (abs(pvi.bmiHeader.biHeight) <> FImageHeight) then
  1111.     // If the image width/height is changed, fail CheckMediaType() to force
  1112.     // the renderer to resize the image.
  1113.   begin
  1114.     Result := E_INVALIDARG;
  1115.     Exit;
  1116.   end;
  1117.   // Don't accept formats with negative height, which would cause the desktop
  1118.   // image to be displayed upside down.
  1119.   if (pvi.bmiHeader.biHeight < 0) then
  1120.   begin
  1121.     Result := E_INVALIDARG;
  1122.     Exit;
  1123.   end;
  1124.   Result := S_OK; // This format is acceptable.
  1125. end;
  1126. //
  1127. // DecideBufferSize
  1128. //
  1129. // This will always be called after the format has been sucessfully
  1130. // negotiated. So we have a look at AMMediaType to see what size image we agreed.
  1131. // Then we can ask for buffers of the correct size to contain them.
  1132. //
  1133. function TBCPushPinDesktop.DecideBufferSize(Allocator: IMemAllocator;
  1134.   Properties: PAllocatorProperties): HRESULT;
  1135. var
  1136.   pvi: PVIDEOINFOHEADER;
  1137.   Actual: ALLOCATOR_PROPERTIES;
  1138. begin
  1139.   if (Allocator = nil) or (Properties = nil) then
  1140.   begin
  1141.     Result := E_POINTER;
  1142.     Exit;
  1143.   end;
  1144.   FFilter.StateLock.Lock;
  1145.   try
  1146.     pvi := AMMediaType.pbFormat;
  1147.     Properties.cBuffers := 1;
  1148.     Properties.cbBuffer := pvi.bmiHeader.biSizeImage;
  1149.     Assert(Properties.cbBuffer <> 0);
  1150.     // Ask the allocator to reserve us some sample memory. NOTE: the function
  1151.     // can succeed (return NOERROR) but still not have allocated the
  1152.     // memory that we requested, so we must check we got whatever we wanted.
  1153.     Result := Allocator.SetProperties(Properties^, Actual);
  1154.     if Failed(Result) then
  1155.       Exit;
  1156.     // Is this allocator unsuitable?
  1157.     if (Actual.cbBuffer < Properties.cbBuffer) then
  1158.     begin
  1159.       Result := E_FAIL;
  1160.       Exit;
  1161.     end;
  1162.     // Make sure that we have only 1 buffer
  1163.     Assert(Actual.cBuffers = 1);
  1164.     Result := S_OK;
  1165.   finally
  1166.     FFilter.StateLock.UnLock;
  1167.   end;
  1168. end;
  1169. //
  1170. // SetMediaType
  1171. //
  1172. // Called when a media type is agreed between filters
  1173. //
  1174. function TBCPushPinDesktop.SetMediaType(MediaType: PAMMediaType): HRESULT;
  1175. var
  1176.   pvi: PVIDEOINFOHEADER;
  1177. begin
  1178.   FFilter.StateLock.Lock;
  1179.   try
  1180.     // Pass the call up to my base class
  1181.     Result := inherited SetMediaType(MediaType);
  1182.     if Succeeded(Result) then
  1183.     begin
  1184.       pvi := AMMediaType.pbFormat;
  1185.       if (pvi = nil) then
  1186.       begin
  1187.         Result := E_UNEXPECTED;
  1188.         Exit;
  1189.       end;
  1190.       // 8-bit palettized,
  1191.       // RGB565, RGB555,
  1192.       // RGB24,
  1193.       // RGB32
  1194.       if pvi.bmiHeader.biBitCount in [8, 16, 24, 32] then
  1195.       begin
  1196.         // Save the current media type and bit depth
  1197.         FMediaType := MediaType^;
  1198.         FCurrentBitDepth := pvi.bmiHeader.biBitCount;
  1199.       end
  1200.       else
  1201.       begin
  1202.         // We should never agree any other media types
  1203.         Assert(False);
  1204.         Result := E_INVALIDARG;
  1205.       end;
  1206.     end;
  1207.   finally
  1208.     FFilter.StateLock.UnLock;
  1209.   end;
  1210. end;
  1211. // This is where we insert the DIB bits into the video stream.
  1212. // FillBuffer is called once for every sample in the stream.
  1213. function TBCPushPinDesktop.FillBuffer(Sample: IMediaSample): HResult;
  1214. var
  1215.   pData: PByte;
  1216.   cbData: Longint;
  1217.   hDib: HBitmap;
  1218.   pvih: PVIDEOINFOHEADER;
  1219.   Start, Stop: REFERENCE_TIME;
  1220.   function min(v1, v2: DWord): DWord;
  1221.   begin
  1222.     if v1 <= v2 then
  1223.       Result := v1
  1224.     else
  1225.       Result := v2;
  1226.   end;
  1227. begin
  1228.   if (Sample = nil) then
  1229.   begin
  1230.     Result := E_POINTER;
  1231.     Exit;
  1232.   end;
  1233.   FSharedState.Lock;
  1234.   try
  1235.     // Access the sample's data buffer
  1236.     Sample.GetPointer(pData);
  1237.     cbData := Sample.GetSize;
  1238.     // Check that we're still using video
  1239.     Assert(IsEqualGUID(AMMediaType.formattype, FORMAT_VideoInfo));
  1240.     pvih := AMMediaType.pbFormat;
  1241.     // Copy the DIB bits over into our filter's output buffer.
  1242.      // Since sample size may be larger than the image size, bound the copy size.
  1243.     pVih.bmiHeader.biSizeImage := min(pVih.bmiHeader.biSizeImage, cbData);
  1244.     hDib := CopyScreenToBitmap(FScreenRect, pData, @pVih.bmiHeader);
  1245.     if (hDib <> 0) then
  1246.       DeleteObject(hDib);
  1247.     // Set the timestamps that will govern playback frame rate.
  1248.     // If this file is getting written out as an AVI,
  1249.     // then you'll also need to configure the AVI Mux filter to
  1250.     // set the Average Time Per Frame for the AVI Header.
  1251.     // The current time is the sample's start
  1252.     Start := FFrameNumber * FFrameLength;
  1253.     Stop := Start + FFrameLength;
  1254.     Sample.SetTime(@Start, @Stop);
  1255.     Inc(FFrameNumber);
  1256.     // Set TRUE on every sample for uncompressed frames
  1257.     Sample.SetSyncPoint(True);
  1258.     Result := S_OK;
  1259.   finally
  1260.     FSharedState.UnLock;
  1261.   end;
  1262. end;
  1263. function TBCPushPinDesktop.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;
  1264. begin
  1265.   Result := E_FAIL;
  1266. end;
  1267. // --- TBCPushSourceBitmap ------------
  1268. constructor TBCPushSourceDesktop.Create(ObjName: string; Unk: IUnKnown;
  1269.   out hr: HRESULT);
  1270. begin
  1271.   inherited Create(ObjName, Unk, CLSID_PushSourceDesktop);
  1272.   // The pin magically adds itself to our pin array.
  1273.   FPin := TBCPushPinDesktop.Create(hr, Self);
  1274.   if (hr <> S_OK) then
  1275.     if (FPin = nil) then
  1276.       hr := E_OUTOFMEMORY;
  1277. end;
  1278. constructor TBCPushSourceDesktop.CreateFromFactory(Factory: TBCClassFactory;
  1279.   const Controller: IUnknown);
  1280. var
  1281.   hr: HRESULT;
  1282. begin
  1283.   Create(Factory.Name, Controller, hr);
  1284. end;
  1285. destructor TBCPushSourceDesktop.Destroy;
  1286. begin
  1287.   FreeAndNil(FPin);
  1288.   inherited;
  1289. end;
  1290. initialization
  1291.   // provide entries in the CFactoryTemplate array
  1292.   TBCClassFactory.CreateFilter(TBCPushSourceBitmap, PushBitmapName,
  1293.     CLSID_PushSourceBitmap, CLSID_LegacyAmFilterCategory,
  1294.     MERIT_DO_NOT_USE, 1, @sudOutputPinBitmap
  1295.     );
  1296.   TBCClassFactory.CreateFilter(TBCPushSourceBitmapSet, PushBitmapSetName,
  1297.     CLSID_PushSourceBitmapSet, CLSID_LegacyAmFilterCategory,
  1298.     MERIT_DO_NOT_USE, 1, @sudOutputPinBitmapSet
  1299.     );
  1300.   TBCClassFactory.CreateFilter(TBCPushSourceDesktop, PushDesktopName,
  1301.     CLSID_PushSourceDesktop, CLSID_LegacyAmFilterCategory,
  1302.     MERIT_DO_NOT_USE, 1, @sudOutputPinDesktop
  1303.     );
  1304. end.