zoukankan      html  css  js  c++  java
  • 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) // select format with maximum bitrate
            then
            begin
              selectedIndex := I;
              bitRate       := ih^.dwBitRate;
            end;
            // 删除媒体类型(媒体类型)
            DeleteMediaType(pmt);
          end;
          if selectedIndex > -1 then
          begin
            streamConfig.GetStreamCaps(selectedIndex, pmt, pSCC);
            try
              // 配置流.设置格式()
              streamConfig.SetFormat(pmt^);
            finally
              DeleteMediaType(pmt);
            end;
          end;
        finally
          // 释放
          FreeMem(pSCC);
        end;
      end;
      Result := True;
    end;
    
    // 枚举所有USB摄像头
    { 枚举所有视频输入设备 }
    procedure EnumAllUSBCamera(strsList: TStrings);
    var
      SysDevEnum: ICreateDevEnum;
      EnumCat   : IEnumMoniker;
      hr        : Integer;
      Moniker   : IMoniker;
      Fetched   : ULONG;
      PropBag   : IPropertyBag;
      strName   : OleVariant;
      strGuid   : OleVariant;
      III       : Integer;
      puInfo    : PVideoInputInfo;
      intIndex  : Integer;
    begin
      // 共同创建实例
      { 创建系统枚举器对象 }
      hr := CocreateInstance(CLSID_SystemDeviceEnum, nil, CLSCTX_INPROC, IID_ICreateDevEnum, SysDevEnum);
      // 如果 hr <> 0
      if hr <> S_OK then
        Exit;
    
      // 系统开发人员枚举.创建类枚举器(CLSID视频输入设备类别,枚举猫,0)
      { 用指定的 Filter 目录类型创建一个枚举器,并获得 IEnumMoniker 接口; }
      hr := SysDevEnum.CreateClassEnumerator(CLSID_VideoInputDeviceCategory, EnumCat, 0);
      if hr <> S_OK then
        Exit;
    
      try
        { 释放内存 }
        // 如果 数组的成员数量 > 0
        if strsList.Count > 0 then
        begin
          for III := 0 to strsList.Count - 1 do
          begin
            // 释放(视频格式信息(对象))
            FreeMem(PVideoFormatInfo(strsList.Objects[III]));
          end;
        end;
        // 数组释放
        strsList.Clear;
    
        { 获取指定类型目录下所有设备标识 }
        while (EnumCat.Next(1, Moniker, @Fetched) = S_OK) do
        begin
          Moniker.BindToStorage(nil, nil, IID_IPropertyBag, PropBag);
          PropBag.Read('CLSID', strGuid, nil);
          PropBag.Read('FriendlyName', strName, nil);
          New(puInfo);
          puInfo^.id      := TGUID(strGuid);
          puInfo^.strName := ShortString(strName);
          puInfo^.index   := 0;
          if strsList.IndexOf(strName) = -1 then
          begin
            strsList.AddObject(strName, TObject(puInfo));
          end
          else
          begin
            { 相同名称的 USBCamera 相机,<有可能有多个名称重复的相机> }
            intIndex      := GetMaxIndex(strsList, strName);
            puInfo^.index := intIndex + 1;
            strsList.AddObject(strName + format('(%d)', [puInfo^.index]), TObject(puInfo));
          end;
          PropBag := nil;
          Moniker := nil;
        end;
    
      finally
        EnumCat    := nil;
        SysDevEnum := nil;
      end;
    end;
    
    // 遍历出来所有支持的格式【各种分辨率】参数1:传进来的名字,参数2:选择名字的下标,参数3:枚举出来的格式放在那里
    { 枚举视频支持格式 }
    function EnumVideoFormat(const strFriendlyName: String; const intIndex: Integer; strsList: TStrings): Boolean;
    var
      SysDevEnum          : IBaseFilter;
      CaptureGraphBuilder2: ICaptureGraphBuilder2;
      iunk                : IUnknown;
      fStreamConfig       : IAMStreamConfig;
      piCount, piSize     : Integer;
      III                 : Integer;
      pmt                 : PAMMediaType;
      pSCC                : PVideoStreamConfigCaps;
      pvInfo              : PVideoFormatInfo;
    begin
      Result := False;
    
      // Filter 实际上它不是物理摄像头,只是模拟摄像头设备可以采集图像
      { 获取指定USB摄像头的 Filter }
      SysDevEnum := CreateFilter(CLSID_VideoInputDeviceCategory, AnsiString(strFriendlyName), intIndex);
      if SysDevEnum = nil then
        Exit;
    
      { 释放内存 }
      if strsList.Count > 0 then
      begin
        for III := 0 to strsList.Count - 1 do
        begin
          FreeMem(PVideoFormatInfo(strsList.Objects[III]));
        end;
      end;
      strsList.Clear;
    
      { 创建 ICaptureGraphBuilder2 接口 }
      if Failed(CocreateInstance(CLSID_CaptureGraphBuilder2, nil, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, CaptureGraphBuilder2)) then
        Exit;
    
      { 获取 IID_IAMStreamConfig 接口 }
      if Failed(CaptureGraphBuilder2.FindInterface(nil, nil, SysDevEnum, IID_IAMStreamConfig, iunk)) then
        Exit;
    
      { 获取 IAMStreamConfig 媒体类型接口 }
      if Failed(iunk.QueryInterface(IID_IAMStreamConfig, fStreamConfig)) then
        Exit;
    
      if Failed(fStreamConfig.GetNumberOfCapabilities(piCount, piSize)) then
        Exit;
    
      if piCount <= 0 then
        Exit;
    
      // pSCC 视频流配置上线
      { 枚举支持的视频格式 }
      pSCC := AllocMem(piSize);
      try
        for III := 0 to piCount - 1 do
        begin
          if fStreamConfig.GetStreamCaps(III, pmt, pSCC) = S_OK then
          begin
            try
              New(pvInfo); { 注意释放内存 }
              pvInfo^.Frame   := PVIDEOINFOHEADER(pmt^.pbFormat)^.AvgTimePerFrame;
              pvInfo^.id      := pmt^.formattype;
              pvInfo^.iWidth  := pSCC^.MaxOutputSize.cx;
              pvInfo^.iHeight := pSCC^.MaxOutputSize.cy;
              pvInfo^.iMod    := pmt^.subtype;
              pvInfo^.format  := VideoMediaSubTypeToStr(pmt^.subtype);
              strsList.AddObject(format('类型:%s  分辨率:%4d×%4d', [pvInfo^.format, pvInfo^.iWidth, pvInfo^.iHeight]), TObject(pvInfo));
            finally
              DeleteMediaType(pmt);
            end;
          end;
        end;
      finally
        FreeMem(pSCC);
      end;
    
      SysDevEnum           := nil;
      CaptureGraphBuilder2 := nil;
      fStreamConfig        := nil;
    
      Result := True;
    end;
    
    /// <summary>
    /// 普通影片
    /// </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视频输入信息</param>
    /// <param name="pf">P视频格式信息</param>
    /// <param name="pnl">搭载视频的面板【panel面板控件】</param>
    /// <param name="strSaveFileName">保存文件路径名</param>
    /// <param name="bRecord">是否开始录像</param>
    /// <param name="bSnapBmp">是否开启截图功能</param>
    /// <returns>布尔类型</returns>
    function CommonVideo(var FIGraphBuilder: IGraphBuilder;               // FI图形生成器
      var FICaptureGraphBuilder2: ICaptureGraphBuilder2;                  // FI捕获图生成器
      var FSysDevEnum: IBaseFilter;                                       // FSys开发枚举
      var FIVideoWindow: IVideoWindow;                                    // FI视频窗口
      var FIMediaControl: IMediaControl;                                  // FI媒体控制
      var FISampleGrabber: ISampleGrabber;                                // FI样品采集器
      pv: PVideoInputInfo; pf: PVideoFormatInfo;                          // pv:P视频输入信息、pf:P视频格式信息
      pnl: TPanel;                                                        // 搭载视频的面板【panel面板控件】
      const strSaveFileName: string = ''; const bRecord: Boolean = False; // 录像 【strSaveFileName:保存文件路径名、bRecord:是否开始录像】
      const bSnapBmp: Boolean = False                                     // 截图 【bSnapBmp:是否开启截图功能】
      ): Boolean;
    var
      SampleGrabberFilter: IBaseFilter;
      mt                 : TAMMediaType;
      multiplexer        : IBaseFilter;
      Writer             : IFileSinkFilter;
    begin
      Result := False;
    
      // 创建 图形生成器 接口
      { 创建 IGraphBuilder 接口 }
      if Failed(CocreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC, IID_IGraphBuilder, FIGraphBuilder)) then
        Exit;
    
      // 创建 捕获图生成器 接口
      { 创建 ICaptureGraphBuilder2 接口 }
      if Failed(CocreateInstance(CLSID_CaptureGraphBuilder2, nil, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, FICaptureGraphBuilder2)) then
        Exit;
    
      // SetFiltergraph:设置滤镜
      { 调用 ICaptureGraphBuilder2 的 SetFilterGraph 方法将 FilterGraph 加入到 Builder 中 }
      if Failed(FICaptureGraphBuilder2.SetFiltergraph(FIGraphBuilder)) then
        Exit;
    
      // 调用本单元的 CreateFilter 创建 Filter
      { 获取指定USB摄像头的 Filter }
      FSysDevEnum := CreateFilter(CLSID_VideoInputDeviceCategory, AnsiString(pv^.strName), pv^.index);
      if FSysDevEnum = nil then
        Exit;
    
      // 调用本单元 SetMediaType 【设置指定 Filter 的媒体格式类型】
      { 设置指定 Filter 的媒体格式类型 }
      if not SetMediaType(FSysDevEnum, pf^.iWidth, pf^.iHeight, pf^.format) then
        Exit;
    
      { 将视频捕捉 Filter 添加到 Filter 图中 }
      if Failed(FIGraphBuilder.AddFilter(FSysDevEnum, 'VideoCapture')) then
        Exit;
    
      { 如果需要截图功能 }
      if bSnapBmp then
      begin
        // 共同创建实例(,,,,样品采集器Filter)
        CocreateInstance(CLSID_SampleGrabber, nil, CLSCTX_INPROC, IID_IBaseFilter, SampleGrabberFilter);
        // FI图形生成器.添加 Filter(样品采集器Filter,‘样品采集器’)
        FIGraphBuilder.AddFilter(SampleGrabberFilter, 'SampleGrabber');
        // 样品采集器Filter.查询接口()
        SampleGrabberFilter.QueryInterface(IID_ISampleGrabber, FISampleGrabber);
        zeromemory(@mt, sizeof(AM_MEDIA_TYPE));
        mt.majortype := MEDIATYPE_Video;
        mt.subtype   := MEDIASUBTYPE_RGB24;     // 24位,位图格式输出
        FISampleGrabber.SetMediaType(@mt);      //
        // 样品采集器.设置缓冲液样本()
        FISampleGrabber.SetBufferSamples(True); // 允许从 Buffer 中获取数据
        { 渲染预览视频PIN }
        if Failed(FICaptureGraphBuilder2.RenderStream(@PIN_CATEGORY_PREVIEW, @MEDIATYPE_Video, FSysDevEnum, SampleGrabberFilter, nil)) then
          Exit;
      end
      else
      begin
        { 渲染预览视频PIN }
        if Failed(FICaptureGraphBuilder2.RenderStream(@PIN_CATEGORY_PREVIEW, @MEDIATYPE_Video, FSysDevEnum, nil, nil)) then
          Exit;
      end;
    
      { 如果是视频录制 }
      if bRecord then
      begin
        // SetOutputFileName:设置输出文件名
        { 视频录制文件保持路径 }
        if Failed(FICaptureGraphBuilder2.SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(strSaveFileName), multiplexer, Writer)) then
          Exit;
    
        // RenderStream:渲染流
        if Failed(FICaptureGraphBuilder2.RenderStream(@PIN_CATEGORY_CAPTURE, @MEDIATYPE_Video, FSysDevEnum, nil, multiplexer)) then
          Exit;
      end;
    
      { 设置视频预览窗口 }
      if Failed(FIGraphBuilder.QueryInterface(IID_IVideoWindow, FIVideoWindow)) then
        Exit;
    
      { 设置视频播放的WINDOWS窗口 }
      if Failed(FIVideoWindow.put_Owner(pnl.Handle)) then
        Exit;
    
      if Failed(FIVideoWindow.put_windowstyle(WS_CHILD or WS_Clipsiblings)) then
        Exit;
    
      { 设置视频尺寸 }
      if Failed(FIVideoWindow.SetWindowposition(0, 0, pnl.Width, pnl.Height)) then
        Exit;
    
      { 得到IMediaControl接口,用于控制流播放 }
      if Failed(FIGraphBuilder.QueryInterface(IID_IMediaControl, FIMediaControl)) then
        Exit;
    
      Result := True;
    end;
    
    { 视频预览 }
    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;
    begin
      Result := CommonVideo(FIGraphBuilder, FICaptureGraphBuilder2, FSysDevEnum, FIVideoWindow, FIMediaControl, FISampleGrabber, pv, pf, pnl, '', False, bSnapBmp);
    end;
    
    { 视频录制 }
    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;
    begin
      Result := CommonVideo(FIGraphBuilder, FICaptureGraphBuilder2, FSysDevEnum, FIVideoWindow, FIMediaControl, FISampleGrabber, pv, pf, pnl, strSaveFileName, True, True);
    end;
    
    end.
    好的代码像粥一样,都是用时间熬出来的
  • 相关阅读:
    FreeCommander 学习手册
    String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别 (String系列之1)
    StringBuffer 详解 (String系列之3)
    StringBuilder 详解 (String系列之2)
    java io系列26之 RandomAccessFile
    java io系列25之 PrintWriter (字符打印输出流)
    java io系列24之 BufferedWriter(字符缓冲输出流)
    java io系列23之 BufferedReader(字符缓冲输入流)
    java io系列22之 FileReader和FileWriter
    java io系列21之 InputStreamReader和OutputStreamWriter
  • 原文地址:https://www.cnblogs.com/jijm123/p/14328285.html
Copyright © 2011-2022 走看看