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