参考:https://blog.csdn.net/qq_44111597/article/details/112787624
RAD Studio 10.2.3 测试√
目录
声明:
uses
DirectShow9, untUSBCamera, Winapi.ActiveX
private
{ Private declarations }
// 系统开发人员枚举【基本 Filter】
mBaseFilter : IBaseFilter;
// FI图形生成器【图形生成器】
mGraphBuilder : IGraphBuilder;
// FI媒体控制【媒体控制】
mMediaControl : IMediaControl;
// FI视频窗口【视频窗口】
mVideoWindow : IVideoWindow;
// FI捕获图生成器2【捕获图生成器2】
mCaptureGraphBuilder2: ICaptureGraphBuilder2;
// FI样品采集器【样品采集器】
mSampleGrabber : ISampleGrabber;
procedure FreeGraph;
procedure mSnapBmp;
function ShowFilterPropertyPages(filter: IBaseFilter; hFormHandle: THandle): Boolean;
function ShowPinPropertyPages(pin: IPin; hFormHandle: THandle): Boolean;
// 视频预览变量初始化
procedure TForm1.FreeGraph;
begin
mBaseFilter := nil;
mGraphBuilder := nil;
mMediaControl := nil;
mVideoWindow := nil;
mCaptureGraphBuilder2 := nil;
mSampleGrabber := nil;
end;
// 截图 -- 保存
procedure TForm1.mSnapBmp;
var
pfs : TFilterState;
mt : TAMMediaType;
hr : HResult;
pBufferSize: Integer;
pBuffer : PByte;
bmp : TBitmap;
vi : PVideoInfoHeader;
begin
if mMediaControl = nil then
Exit;
// FI媒体控制.获取状态(时间,Filter状态)
mMediaControl.GetState(1000, pfs);
if pfs = State_Stopped then
Exit;
{ 获取媒体类型 }
// = 样品采集器.获取连接的媒体类型(媒体类型)
hr := mSampleGrabber.GetConnectedMediaType(mt);
if hr <> S_OK then
Exit;
// 如果 媒体类型.格式 = nil
if mt.pbFormat = nil then
Exit;
// = 视频信息标题(媒体类型.格式)
vi := PVideoInfoHeader(mt.pbFormat);
{ 获取当前帧数据大小 }
// = 样品采集器.获取当前缓冲区(p缓冲区大小,nil)
hr := mSampleGrabber.GetCurrentBuffer(pBufferSize, nil);
if hr <> S_OK then
Exit;
{ 分配内存大小 }
pBuffer := AllocMem(pBufferSize);
try
{ 再一次获取当前帧,获取图像数据 }
hr := mSampleGrabber.GetCurrentBuffer(pBufferSize, pBuffer);
if hr <> S_OK then
Exit;
{ 创建位图 }
bmp := TBitmap.Create;
try
// bmp.像素格式 =
bmp.PixelFormat := pf24bit;
bmp.width := vi^.bmiHeader.biWidth;
bmp.height := vi^.bmiHeader.biHeight;
SetBitmapBits(bmp.Handle, vi^.bmiHeader.biSizeImage, pBuffer);
bmp.Canvas.CopyRect(bmp.Canvas.ClipRect, bmp.Canvas, Rect(0, bmp.height, bmp.width, 0));
// 把位图信息放在img控件中
bmp.SaveToFile(ExtractFilePath(Paramstr(0)) + 'Temp\' + FormatDateTime('YYYYMMDDhhmmss', Now()) + '.jpg');
finally
bmp.Free;
end;
finally
FreeMem(pBuffer);
end;
end;
// 显示 Filter 属性页【调用 Filter 的属性页,窗口的 Windows 本身的】
function TForm1.ShowFilterPropertyPages(filter: IBaseFilter; hFormHandle: THandle): Boolean;
var
// 指定属性页
pSpecify: ISpecifyPropertyPages;
// 全局唯一标识符
caGUID : TCAGUID;
begin
Result := False;
pSpecify := nil;
filter.QueryInterface(ISpecifyPropertyPages, pSpecify);
if pSpecify <> nil then
begin
// 指定属性页.获取页面(全局唯一标识符)
pSpecify.GetPages(caGUID);
pSpecify := nil;
Result := OleCreatePropertyFrame(hFormHandle, 0, 0, '', 1, Pointer(@filter), caGUID.cElems, PGUID(caGUID.pElems), 0, 0, nil) = S_OK;
CoTaskMemFree(caGUID.pElems);
end;
end;
function TForm1.ShowPinPropertyPages(pin: IPin; hFormHandle: THandle): Boolean;
var
// 指定属性页
pSpecify: ISpecifyPropertyPages;
// 全局唯一标识符
caGUID : TCAGUID;
begin
Result := False;
pSpecify := nil;
// Filter.查询接口(类型,值【指定属性页】)
pin.QueryInterface(ISpecifyPropertyPages, pSpecify);
if pSpecify <> nil then
begin
// 指定属性页.获取页面(全局唯一标识符)
pSpecify.GetPages(caGUID);
pSpecify := nil;
Result := OleCreatePropertyFrame(hFormHandle, 0, 0, '', 1, Pointer(@pin), caGUID.cElems, PGUID(caGUID.pElems), 0, 0, nil) = S_OK;
CoTaskMemFree(caGUID.pElems);
end;
end;
1.展示设备
begin
{ 视频预览变量初始化 }
FreeGraph;
// 获取所有USB摄像头,存入下面控件的列表中
EnumAllUSBCamera(ListBox3.Items);
end;
2.展示格式
begin
if ListBox3.ItemIndex = -1 then
begin
ShowMessage('请选择设备!');
exit;
end;
{ 枚举视频支持格式 }
if not EnumVideoFormat(String(PVideoInputInfo(ListBox3.Items.Objects[ListBox3.ItemIndex])^.strName), PVideoInputInfo(ListBox3.Items.Objects[ListBox3.ItemIndex])^.index, ListBox4.Items) then
Exit;
ListBox4.ItemIndex := 0;
ListBox4.SetFocus;
end;
3.打开预览
begin
if mMediaControl = nil then
begin
{ 视频预览 }
if USBVideoPreview(mGraphBuilder, mCaptureGraphBuilder2, mBaseFilter, mVideoWindow, mMediaControl, mSampleGrabber, PVideoInputInfo(ListBox3.Items.Objects[ListBox3.ItemIndex]), PVideoFormatInfo(ListBox4.Items.Objects[ListBox4.ItemIndex]), Panel1, True) then
begin
Button14.Caption := '3.停止预览';
mMediaControl.Run;
end;
end
else
begin
Button14.Caption := '3.打开预览';
mMediaControl.Stop;
FreeGraph;
end;
end;
4.保存图片至本地
5.色彩选择 和 通道选项
// 色彩选择
begin
ShowFilterPropertyPages(mBaseFilter, Handle);
end;
// 通道选项
var
pin: IPin;
begin
mCaptureGraphBuilder2.FindPin(mBaseFilter, PINDIR_OUTPUT, nil, nil, False, 0, pin);
ShowPinPropertyPages(pin, Handle);
end;
附件:untUSBCamera单元文件
unit untUSBCamera;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, DirectShow9, ActiveX, Dialogs, StdCtrls, ExtCtrls;
type
PVideoInputInfo = ^TVideoInputInfo;
PVideoFormatInfo = ^TVideoFormatInfo;
{ 视频输入设备 }
TVideoInputInfo = record
id: TGUID;
strName: ShortString;
index: Integer;
end;
{ 视频支持格式 }
TVideoFormatInfo = record
id: TGUID;
iWidth, iHeight: Integer;
iMod: TGUID;
Frame: Int64;
format: ShortString;
end;
type
IAMStreamConfig = interface(IUnknown)
['{C6E13340-30AC-11d0-A18C-00A0C9118956}']
function SetFormat(const pmt: TAMMediaType): HResult; stdcall;
function GetFormat(out ppmt: PAMMediaType): HResult; stdcall;
function GetNumberOfCapabilities(out piCount, piSize: Integer): HResult; stdcall;
{ Delphi 声明有误,修改声明 }
function GetStreamCaps(iIndex: Integer; var ppmt: PAMMediaType; pSCC: PVideoStreamConfigCaps): HResult; stdcall;
end;
ISampleGrabber = interface(IUnknown)
['{6B652FFF-11FE-4FCE-92AD-0266B5D7C78F}']
function SetOneShot(OneShot: BOOL): HResult; stdcall;
{ Delphi 声明有误,修改声明 }
function SetMediaType(pType: PAMMediaType): HResult; stdcall;
function GetConnectedMediaType(out pType: TAMMediaType): HResult; stdcall;
function SetBufferSamples(BufferThem: BOOL): HResult; stdcall;
function GetCurrentBuffer(var pBufferSize: longint; pBuffer: Pointer): HResult; stdcall;
function GetCurrentSample(out ppSample: IMediaSample): HResult; stdcall;
function SetCallback(pCallback: ISampleGrabberCB; WhichMethodToCallback: longint): HResult; stdcall;
end;
{ 枚举所有视频输入设备 }
/// <summary>
/// 枚举所有视频输入设备
/// </summary>
/// <param name="strsList">设备名称展示的存放地方</param>
procedure EnumAllUSBCamera(strsList: TStrings);
{ 枚举视频支持格式 }
/// <summary>
/// 枚举视频支持格式【遍历出支持的视频格式】
/// </summary>
/// <param name="strFriendlyName">传进来的设备名称</param>
/// <param name="intIndex">设备的下标序号</param>
/// <param name="strsList">格式展示的box</param>
/// <returns>布尔类型</returns>
function EnumVideoFormat(const strFriendlyName: String; const intIndex: Integer; strsList: TStrings): Boolean;
{ 视频预览 }
/// <summary>
/// 视频预览【USB视频预览】
/// </summary>
/// <param name="FIGraphBuilder">FI图形生成器</param>
/// <param name="FICaptureGraphBuilder2">FI捕获图生成器2</param>
/// <param name="FSysDevEnum">FSys开发枚举【mBaseFilter】</param>
/// <param name="FIVideoWindow">FI视频窗口</param>
/// <param name="FIMediaControl">FI媒体控制</param>
/// <param name="FISampleGrabber">FI样品采集器</param>
/// <param name="pv">P视频输入信息(USB摄像头.列表.对象[pv])</param>
/// <param name="pf">P视频输入信息(USB摄像头支持格式.列表.对象[pf])</param>
/// <param name="pnl">pnl视频【放视频的面板】</param>
/// <param name="bSnapBmp">是否需要截屏功能</param>
/// <returns>布尔类型</returns>
function USBVideoPreview(var FIGraphBuilder: IGraphBuilder; var FICaptureGraphBuilder2: ICaptureGraphBuilder2; var FSysDevEnum: IBaseFilter; var FIVideoWindow: IVideoWindow; var FIMediaControl: IMediaControl; var FISampleGrabber: ISampleGrabber; pv: PVideoInputInfo; pf: PVideoFormatInfo; pnl: TPanel; const bSnapBmp: Boolean = True): Boolean;
{ 视频录制 }
/// <summary>
/// 视频录制【USB视频录制】
/// </summary>
/// <param name="FIGraphBuilder">FI图形生成器</param>
/// <param name="FICaptureGraphBuilder2">FI捕获图生成器</param>
/// <param name="FSysDevEnum">FSys开发枚举</param>
/// <param name="FIVideoWindow">FI视频窗口</param>
/// <param name="FIMediaControl">FI媒体控制</param>
/// <param name="FISampleGrabber">FI样品采集器</param>
/// <param name="pv">P视频输入信息(USB摄像头.列表.对象[pv])</param>
/// <param name="pf">P视频输入信息(USB摄像头支持格式.列表.对象[pf])</param>
/// <param name="pnl">pnl视频【放视频的面板】</param>
/// <param name="strSaveFileName">视频文件保存的位置</param>
/// <returns>布尔类型</returns>
function USBVideoRecord(var FIGraphBuilder: IGraphBuilder; var FICaptureGraphBuilder2: ICaptureGraphBuilder2; var FSysDevEnum: IBaseFilter; var FIVideoWindow: IVideoWindow; var FIMediaControl: IMediaControl; var FISampleGrabber: ISampleGrabber; pv: PVideoInputInfo; pf: PVideoFormatInfo; pnl: TPanel; const strSaveFileName: String): Boolean;
implementation
const
IID_IPropertyBag: TGUID = '{55272A00-42CB-11CE-8135-00AA004BB851}';
c_arrVideoFormatGUID: array [0 .. 35] of PGUID = ( //
@MEDIASUBTYPE_CLPL, @MEDIASUBTYPE_YUYV, @MEDIASUBTYPE_IYUV, @MEDIASUBTYPE_YVU9, @MEDIASUBTYPE_Y411, @MEDIASUBTYPE_Y41P, //
@MEDIASUBTYPE_YUY2, @MEDIASUBTYPE_YVYU, @MEDIASUBTYPE_UYVY, @MEDIASUBTYPE_Y211, @MEDIASUBTYPE_YV12, @MEDIASUBTYPE_CLJR, //
@MEDIASUBTYPE_IF09, @MEDIASUBTYPE_CPLA, @MEDIASUBTYPE_MJPG, @MEDIASUBTYPE_TVMJ, @MEDIASUBTYPE_WAKE, @MEDIASUBTYPE_CFCC, //
@MEDIASUBTYPE_IJPG, @MEDIASUBTYPE_Plum, @MEDIASUBTYPE_DVCS, @MEDIASUBTYPE_DVSD, @MEDIASUBTYPE_MDVF, @MEDIASUBTYPE_RGB1, //
@MEDIASUBTYPE_RGB4, @MEDIASUBTYPE_RGB8, @MEDIASUBTYPE_RGB565, @MEDIASUBTYPE_RGB555, @MEDIASUBTYPE_RGB24, @MEDIASUBTYPE_RGB32, //
@MEDIASUBTYPE_ARGB1555, @MEDIASUBTYPE_ARGB4444, @MEDIASUBTYPE_ARGB32, @MEDIASUBTYPE_AYUV, @MEDIASUBTYPE_AI44, @MEDIASUBTYPE_IA44 //
);
c_arrVideoFormatName: array [0 .. 35] of AnsiString = ( //
'CLPL', 'YUYV', 'IYUV', 'YVU9', 'Y411', 'Y41P', //
'YUY2', 'YVYU', 'UYVY', 'Y211', 'YV12', 'CLJR', //
'IF09', 'CPLA', 'MJPG', 'TVMJ', 'WAKE', 'CFCC', //
'IJPG', 'Plum', 'DVCS', 'DVSD', 'MDVF', 'RGB1', //
'RGB4', 'RGB8', 'RGB565', 'RGB555', 'RGB24', 'RGB32', //
'ARGB1555', 'ARGB4444', 'ARGB32', 'AYUV', 'AI44', 'IA44' //
);
function GetMaxIndex(const strsList: TStrings; const strUSBCameraName: string): Integer;
var
III, Count: Integer;
begin
Result := 0;
Count := strsList.Count;
for III := 0 to Count - 1 do
begin
if CompareText(String(PVideoInputInfo(strsList.Objects[III])^.strName), strUSBCameraName) = 0 then
begin
Result := Result + 1;
end;
end;
end;
// 创建 Filter【Filter 实际上它不是物理摄像头,只是模拟摄像头设备可以采集图像】
/// <summary>
/// 创建 Filter【Filter 实际上它不是物理摄像头,只是模拟摄像头设备可以采集图像
/// </summary>
/// <param name="gid">全局唯一标识</param>
/// <param name="FriendlyName">摄像头名字</param>
/// <param name="instanceIndex">摄像头下标序号</param>
/// <returns>基本 Filter</returns>
function CreateFilter(gid: TGUID; FriendlyName: AnsiString; instanceIndex: Integer): IBaseFilter;
var
pSysDevEnum: ICreateDevEnum;
pEnumCat : IEnumMoniker;
pMoniker : IMoniker;
cFetched : ULONG;
pPropBag : IPropertyBag;
bc : IBindCtx;
mo : IMoniker;
er : IErrorLog;
ov : OleVariant;
iIndex : Integer;
begin
Result := nil;
// 系统开发人员枚举
pSysDevEnum := nil;
// 枚举猫
pEnumCat := nil;
// 绰号
pMoniker := nil;
// 如果 共同创建实例() = 0
if CocreateInstance(CLSID_SystemDeviceEnum, nil, CLSCTX_INPROC, IID_ICreateDevEnum, pSysDevEnum) = S_OK then
begin
// 系统开发人员枚举.创建类枚举器(gid,枚举猫,0)
if pSysDevEnum.CreateClassEnumerator(gid, pEnumCat, 0) = S_OK then
begin
iIndex := 0;
while (pEnumCat.Next(1, pMoniker, @cFetched) = S_OK) and (cFetched > 0) and (pMoniker <> nil) do
begin
bc := nil;
mo := nil;
pMoniker.BindToStorage(bc, mo, IID_IPropertyBag, pPropBag);
er := nil;
pPropBag.Read('FriendlyName', ov, er);
if AnsiString(ov) = FriendlyName then
begin
if iIndex = instanceIndex then
begin
bc := nil;
mo := nil;
pMoniker.BindToObject(bc, mo, IID_IBaseFilter, Result);
end;
Inc(iIndex);
end;
pPropBag := nil;
pMoniker := nil;
end;
end;
end;
pEnumCat := nil;
pSysDevEnum := nil;
end;
function VideoMediaSubTypeToStr(mst: TGUID): AnsiString;
var
I: Integer;
begin
Result := '';
for I := 0 to 35 do
if CompareMem(c_arrVideoFormatGUID[I], @mst, sizeof(TGUID)) then
begin
Result := c_arrVideoFormatName[I];
break;
end;
end;
procedure FreeMediaType(mt: TAMMediaType);
begin
if (mt.cbFormat <> 0) then
begin
CoTaskMemFree(mt.pbFormat);
mt.cbFormat := 0;
mt.pbFormat := nil;
end;
if (mt.pUnk <> nil) then
begin
mt.pUnk := nil;
end;
end;
procedure DeleteMediaType(pmt: PAMMediaType);
begin
if pmt <> nil then
begin
FreeMediaType(pmt^);
CoTaskMemFree(pmt);
end;
end;
// 获取输出引脚
function GetOutputPin(filter: IBaseFilter): IPin;
var
penum: IEnumPins;
f : Integer;
d : PIN_DIRECTION;
begin
Result := nil;
filter.EnumPins(penum);
while (penum.Next(1, Result, @f) = S_OK) and (f > 0) do
begin
if (Result.QueryDirection(d) = S_OK) and (d = PINDIR_OUTPUT) then
begin
{ 找到输出接口,返回 }
Exit;
end;
end;
Result := nil;
end;
// Str到视频媒体子类型(格式)
function StrToVideoMediaSubType(ss: AnsiString): TGUID;
var
I: Integer;
begin
Result := c_arrVideoFormatGUID[0]^;
for I := 0 to 35 do
if ss = c_arrVideoFormatName[I] then
begin
Result := c_arrVideoFormatGUID[I]^;
break;
end;
end;
function CompareGUID(const g1, g2: TGUID): Boolean;
begin
Result := CompareMem(@g1, @g2, sizeof(TGUID));
end;
// 设置媒体类型(基本Filter,宽,高,格式)
function SetMediaType(filter: IBaseFilter; Width, Height: Integer; format: AnsiString): Boolean;
var
pmt : PAMMediaType;
piCount, piSize: Integer;
I : Integer;
pSCC : PVideoStreamConfigCaps;
streamConfig : IAMStreamConfig;
outPin : IPin;
formatID : TGUID;
selectedIndex : Integer;
ih : PVIDEOINFOHEADER;
bitRate : dword;
begin
Result := False;
if (Width = 0) or (Height = 0) then
Exit;
// 获取输出引脚
outPin := GetOutputPin(filter);
// 查询界面(IID_IAM流配置,流配置)
outPin.QueryInterface(IID_IAMStreamConfig, streamConfig);
// 如果 已分配(流配置)
if assigned(streamConfig) then
begin
// 选定索引 = -1
selectedIndex := -1;
// 比特率 = 0
bitRate := 0;
// 格式ID = Str到视频媒体子类型(格式)
formatID := StrToVideoMediaSubType(format);
// 配置流.获取功能数量(数量,大小)
streamConfig.GetNumberOfCapabilities(piCount, piSize);
// getmen(视频流配置上限,大小)
getmem(pSCC, piSize);
try
for I := 0 to piCount - 1 do
begin
// 配置流.获取流上限(选定索引,媒体类型,视频流配置上限)
streamConfig.GetStreamCaps(I, pmt, pSCC);
// 视频信息头
ih := Pointer(pmt^.pbFormat);
// 选择最大比特率的格式
if (pSCC^.MinOutputSize.cx = Width) and (pSCC^.MinOutputSize.cy = Height) and (ih^.bmiHeader.biWidth = Width) and (ih^.bmiHeader.biheight = Height) and ((format = '') or (CompareGUID(formatID, pmt^.subtype))) and (ih^.dwBitRate > bitRate) // select format with maximum bitrate
then
begin
|
请发表评论