• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Delphi USB摄像头 - 癫狂编程

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

Delphi USB摄像头

参考: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.保存图片至本地

begin
  mSnapBmp;
end;

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)  
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap