📄 upushsource.pas
字号:
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -