Delphi7 下基于SDL2+FFMPEG 实现USB摄像头播放及拍照

目录

 

简述:

准备工作

获取视频设备类表

FFMPEG视频解码

视频播放

拍照

Demo下载



简述:

    delphi7 下进行USB摄像头开发网上的办法一般两种1。基于AVICAP32的windowsAPI 2.基于DSPack 控件。本文将介绍利用FFMPEG+SDL2实现USB摄像头的播放及拍照。

实现效果图如下

准备工作

    注:以下所需资源可以直接从我的网盘下载。

链接:https://pan.baidu.com/s/1IoJvgI8Y5_3DqhCGTvs4Cw
提取码:pdga

  1.  FFMPEG Delphi7 的DLL封装及头文件。这里利用了FFVCL对FFMPEG的封装。可以在  其官网下载FFVCL – Delphi FFmpeg VCL Components(Video Encoder and Video Player)。可以从我的网盘下载。
  2. SDL2 Delphi7的DLL封装及头文件。可以在官网Simple DirectMedia Layer - Homepage下载。也可以从我的网盘下载。
  3. 因为FFMPEG枚举视频设备列表时对中文的不友好。而有些媒体设备的名称为中文,因此采用DSP控件中DirectShow9实现。可以从我的网盘下载。也可以自行百度下载。

获取视频设备类表

         这里借助DSPACK,因为方便。当然也可以直接使用COM访问DirectShow。必须引用的单元文件DSUtil,DirectShow9,ActiveX,mmsystem。

        主要方法:

unit rocmediadev;

interface
uses
  classes,DSUtil,DirectShow9,ActiveX,mmsystem;
type
   
  //媒体格式定义
  TRocMediaFormat = class
  End;
  //视频媒体格式
  TVideoFormat=class(TRocMediaFormat)
  private
    FWidth:Integer;    //视频分辨率
    FHeight:Integer;   //视频分辨率
    FAvgTimePerFrame:Integer; //每帧时间
    FBitRate:Integer;         //码率
  public
    procedure  setFormat(const w,h,atpf,b:Integer);
    constructor Create(const w,h,atpf,b:Integer);
  published
    property Width:Integer read FWidth;
    property Height:Integer read FHeight;
    property AvgTimePerFrame:Integer read FAvgTimePerFrame;
    property BitRate:Integer read FBitRate;
  End;

  //音频媒体格式
  TAudioFormat=class(TRocMediaFormat)
  private
    FAudioFormat:TWAVEFORMATEX;
  public
    procedure  setFormat(const fmt:TWAVEFORMATEX);
    constructor Create(const fmt:TWAVEFORMATEX);
  published
    property AudioFormat:TWAVEFORMATEX read FAudioFormat;
  End;
  
  //设备媒体格式列表
  TRocMediaFormats=class(TList)
  private
    function getFormats(const index: Integer): TRocMediaFormat;
  public
    procedure Add(const frm:TRocMediaFormat);overload;
    procedure Clear;override;
    property  Formats[const index:Integer]:TRocMediaFormat read getFormats;
  End;

  //媒体设备
  TRocMediaDevice=class
  private
    FfriendlyName:String;
    FmonikerName:String;
    FFormats:TRocMediaFormats;
    procedure DsGetOptionDevice(moniker:IMoniker);
  public
    constructor Create(const friendlyName:String;const monikerName:String;const moniker:IMoniker);
    destructor  destroy;override;
  published
    property Formats:TRocMediaFormats read FFormats;
    property FriendlyName:String read FfriendlyName;
    property MonikerName:String read FmonikerName; 
  End;
 
  //设备媒体管理类
  TRocMediaDeviceManager=class(TList)
  private
    function getDevices(const Index: Integer): TRocMediaDevice;
  protected
     procedure DsGetInputDevices(guidValue:TGUID);
  public
     procedure Clear;override;
     property  Devices[const Index:Integer]:TRocMediaDevice read getDevices;
  End;

  //视频媒体管理类
  TRocVideoDeviceManager=class(TRocMediaDeviceManager)
  public
    constructor Create;
  End;
  
  //音频频媒体管理类
  TRocAudioDeviceManager=class(TRocMediaDeviceManager)
  public
    constructor Create;
  End;



implementation



{ TVideoFormat }

constructor TVideoFormat.Create(const w, h, atpf, b: Integer);
begin
  setFormat(w,h,atpf,b);
end;

procedure TVideoFormat.setFormat(const w, h, atpf, b: Integer);
begin
   FWidth  := w;
   FHeight := h;
   FAvgTimePerFrame := atpf;
   FBitRate := b;
end;

{ TAudioFormat }

constructor TAudioFormat.Create(const fmt: TWAVEFORMATEX);
begin
  setFormat(fmt);
end;

procedure TAudioFormat.setFormat(const fmt: TWAVEFORMATEX);
begin
  FAudioFormat := fmt;
end;

{ TRocMediaFormats }

procedure TRocMediaFormats.Add(const frm: TRocMediaFormat);
begin
  inherited Add(frm);
end;

procedure TRocMediaFormats.Clear;
var
  i:Integer;
begin
  for i:=Count-1 downto 0 do
  Begin
    Formats[i].Free;
    inherited Delete(i);
  End;
  inherited;
end;

function TRocMediaFormats.getFormats(
  const index: Integer): TRocMediaFormat;
begin
  result:= TRocMediaFormat(inherited Items[index]);
end;

{ TRocMediaDevice }

constructor TRocMediaDevice.Create(const friendlyName:String;const monikerName:String;const moniker:IMoniker);
begin
  FfriendlyName := friendlyName;
  FmonikerName  := monikerName;
  FFormats:=TRocMediaFormats.Create;
  DsGetOptionDevice(moniker);
end;

destructor TRocMediaDevice.destroy;
begin
  FFormats.Free;
end;

procedure TRocMediaDevice.DsGetOptionDevice(moniker: IMoniker);
var
  filter:IBaseFilter;
  pinEnum:IEnumPins;
  Pin:IPin;
  pinInfo:PIN_INFO;
  pinFetched:longword;
  mtFetched:Longword;
  mtEnum :IEnumMediaTypes;
  mt:PAMMediaType; 
begin
  
  repeat
     moniker.BindToObject(nil,nil,IID_IBaseFilter,filter);
     If FAILED(filter.EnumPins(pinEnum))then break;
     pinEnum.Reset();
     pinFetched:=0;
     while (SUCCEEDED(pinEnum.Next(1,pin,@pinFetched)) and (pinFetched>0)) do
     Begin
        if (pin=nil) then continue;
        if Failed(pin.QueryPinInfo(pinInfo)) then continue;
        if (pinInfo.dir <> PINDIR_OUTPUT) then continue;
        if Failed(pin.EnumMediaTypes(mtEnum)) then break;
        while (SUCCEEDED(mtEnum.Next(1,mt,@mtFetched)) and (mtFetched>0)) do
        Begin
           if IsEqualGUID(mt.formattype,FORMAT_VideoInfo2) then
           Begin
              if (mt.cbFormat >= sizeof(VIDEOINFOHEADER2)) then
              begin
                 with PVideoInfoHeader2(mt.pbFormat)^ do
                 Begin
                   Formats.Add(TVideoFormat.Create(bmiHeader.biWidth,bmiHeader.biHeight,AvgTimePerFrame,dwBitRate));
                 End;
              end;
           End Else Begin
             if IsEqualGUID(mt.formattype,FORMAT_VideoInfo) then
             Begin
                if (mt.cbFormat >= sizeof(VIDEOINFOHEADER)) then
                begin
                   with PVIDEOINFOHEADER(mt.pbFormat)^ do
                   Begin
                     Formats.Add(TVideoFormat.Create(bmiHeader.biWidth,bmiHeader.biHeight,AvgTimePerFrame,dwBitRate));
                   End;
                end;
             End Else Begin
                if IsEqualGUID(mt.formattype,FORMAT_WaveFormatEx) then
                Begin
                  if(mt.cbFormat>=sizeof(TWAVEFORMATEX)) then
                  Begin
                    Formats.Add(TAudioFormat.Create(PWAVEFORMATEX(mt.pbFormat)^) ); 
                  End;
                End;
             End;
           End;
     End;
  End;

  until 1 = 1;

  Pin:=nil;
  filter:=nil;
  pinEnum:=nil;

end;

{ TRocMediaDeviceManager }

procedure TRocMediaDeviceManager.Clear;
var
  I:Integer;
begin
  for i:=Count-1 downto 0 Do
  Begin
    Devices[i].Free;
    inherited Delete(i);
  End;
  inherited;
end;

procedure TRocMediaDeviceManager.DsGetInputDevices(guidValue: TGUID);
var
  sysDev:TSysDevEnum;
  monike:IMoniker;
  I:Integer;
  pOleDisplayName:PWideChar;
  device:TRocMediaDevice;
begin
  sysDev:=TSysDevenum.Create(guidValue);

  for I:=0 to sysDev.CountFilters-1 Do
  Begin
      monike:=SysDev.GetMoniker(I);
      pOleDisplayName := PWideChar(CoTaskMemAlloc(255));
      try
      monike.GetDisplayName(nil,nil,pOleDisplayName);
      device := TRocMediaDevice.Create(sysDev.Filters[I].FriendlyName,
                                       pOleDisplayName,monike);
      inherited Add(device);
    finally
      CoTaskMemFree(pOleDisplayName);
      monike:=nil;
    end; 
  End;
  sysDev.Free;
end;

function TRocMediaDeviceManager.getDevices(
  const Index: Integer): TRocMediaDevice;
begin
  Result:= TRocMediaDevice(inherited Items[index]);
end;

{ TRocVideoDeviceManager }

constructor TRocVideoDeviceManager.Create;
begin
  DsGetInputDevices(CLSID_VideoInputDeviceCategory);
end;

{ TRocAudioDeviceManager }

constructor TRocAudioDeviceManager.Create;
begin
  DsGetInputDevices(CLSID_AudioInputDeviceCategory);
end;

end.

 以上单元文件使用方法:

var
   VideoDeviceManager:TRocVideoDeviceManager; //视频媒体设备管理类
   i:Integer;
   device:TRocMediaDevice;
Begin
   VideoDeviceManager:=TRocVideoDeviceManager.Create;
   try
     for i:=0 to VideoDeviceManager.Count-1 do
     Begin
        device:=VideoDeviceManager.Devices[i];
        //插入设备信息到combobox1下拉列表中
        //combobox2中显示combobox1中当前选中设备的设备格式
        combobox1.Items.AddObject(device.friendlyName,device);
     End;
   finally
     VideoDeviceManager.free;
   end;
End;

//设备被重新选中时,刷新combobox2列表中设备格式信息。
procedure ComboBox1Change(Sender: TObject);
var
  device:TRocMediaDevice;
  j:Integer;
  fmt:TVideoFormat;
begin
  ComboBox2.Items.Clear;
  device:=TRocMediaDevice(ComboBox1.Items.Objects[ComboBox1.ItemIndex]);
  for j:=0 to device.Formats.Count-1 do
  Begin
     fmt := TVideoFormat(device.Formats.Formats[j]);
     ComboBox2.Items.AddObject(Format('%dx%d',[fmt.Width,fmt.Height]),fmt);
  End;
  if(ComboBox2.Items.Count>0) then ComboBox2.ItemIndex:=0;
end;

        

FFMPEG视频解码

  •  FFMPEG+SDL应用级别初始化

         av_register_all()  这是ffmpeg注册复用器。所有基于FFMPEG的应用程序首先必须调用的函数。主要对所支持的“封装/解封”格式,所支持的协议(protocol).所支持的编码及解码器...的注册。

        avdevice_register_all() 这里还需要调用设备注册函数。因为我们需要打开USB摄像头。

        avformat_network_init() 打开网络流注册函数。主要是读取网络流数据。例如RTSP网络实时视频。在以后的文章中另开一篇介绍RTSP有关的视频实现。

        SDL_INIT(Uint32 flags)  这也是所有基于SDL的应用程序所必须调用的初始化函数。

           flags取值:

             SDL_INIT_TIMER:定时器
             SDL_INIT_AUDIO:音频
             SDL_INIT_VIDEO:视频
             SDL_INIT_JOYSTICK:摇杆
             SDL_INIT_HAPTIC:触摸屏
             SDL_INIT_GAMECONTROLLER:游戏控制器
             SDL_INIT_EVENTS:事件
             SDL_INIT_NOPARACHUTE:不捕获关键信号(这个不理解)
             SDL_INIT_EVERYTHING:包含上述所有选项

           用法举例:SDL_INIT(SDL_INIT_VIDEO or SDL_INIT_AUDIO  or SDL_INIT_TIMER);

  • 初始化USB摄像头

        

var
  AVFormatContext:PAVFormatContext;   
  AVCodecContext:PAVCodecContext;     
  Video_s_index:Integer;              //视频流索引号
  Devicename:String;                  //摄像头名称
  FormatName:String;  //vfwcap or dshow 采集方式 
                      // dshow  摄像头、采集卡、麦克风等
                      // vfwcap 主要时摄像头设备

//初始化视频对象
function InitializeVideoObj:boolean;
var
  dn:String;
  InputFromat:PAVInputFormat;
  options:PAVDictionary;
  retCode,i:Integer;
  videoS:PAVStream;
  AVcodec:PAVCodec;
begin 
  result := false;
  repeat
     AVFormatContext := avformat_alloc_context;
     if not assigned(AVFormatContext) Then break;
     dn := 'video=' + Devicename;
     //采集方式 dshow / vfwcap
     InputFromat := av_find_input_format(pchar(FormatName));
     if not assigned(inputFromat) then break;
     options:=nil;
     //设置视频大小
     av_dict_set(@options,'video_size',
                 pchar(inttostr(FWidth)+'x'+inttostr(FHeight)),0);  
     //打开摄像头
     retCode := avformat_open_input(@AVFormatContext,
                                     pchar(UTF8EnCode(dn)),InputFromat,@options);
     if retCode<0 Then  break;
     options:=nil;
     //查找视频流索引
     retCode := avformat_find_stream_info(AVFormatContext,@options);
     if retCode < 0 Then break;
     video_s_index := -1;
     for i := 0 to AVFormatContext^.nb_streams - 1 do
     Begin
         videoS := PPtrIdx(AVFormatContext^.streams,i);
         if videoS^.codec^.codec_type=AVMEDIA_TYPE_VIDEO then
         Begin
            video_s_index:=i;
            break;
         End;
     End;
    if video_s_index = -1 break;    
    //获取解码器并打开编码器
    AVcodec:=avcodec_find_decoder(videoS^.codec^.codec_id);    
    if not assigned(AVcodec) then break;
    AVCodecContext:=avcodec_alloc_context3(AVcodec);
    if not assigned(AVCodecContext) then break;
    avcodec_parameters_to_context(AVCodecContext,videoS^.codecpar);
    retCode:=AVCodec_Open2(AVCodecContext,AVcodec,nil);
    if retCode<0 Then break;
    result := true;
  until 1=1;
  
end;

   调用avformat_open_input() 打开输入流(这里只USB摄像头),并读取视频流头部信息。这里需要注意的是摄像头名称必须转换为UTF8。

  • 初始化过滤器

  采集到的视频数据统一转换为BGR24格式。方便拍照转为位图图片。这里采用了AVFilter过滤器实现。

  

var
    filter_graph: PAVFilterGraph;
    buffersink_ctx: PAVFilterContext;
    buffersrc_ctx: PAVFilterContext;

function initializeFilter:boolean;
const
  pix_fmts:TAVPixelFormats = (AV_PIX_FMT_BGR24,AV_PIX_FMT_NONE);
var
  args: array[0..512-1] of AnsiChar;
  buffersrc: PAVFilter;
  buffersink: PAVFilter;
  outputs: PAVFilterInOut;
  inputs: PAVFilterInOut;
  ret:integer;
  videoS:PAVStream;
  filters_descr:String;
begin
  result := false;
  buffersrc  := avfilter_get_by_name('buffer');
  buffersink := avfilter_get_by_name('buffersink');
  outputs := avfilter_inout_alloc();
  inputs  := avfilter_inout_alloc();
  filter_graph := avfilter_graph_alloc();
  try
    repeat
       if not Assigned(outputs) or not Assigned(inputs) or 
          not Assigned(filter_graph) then break;
       videoS := PPtrIdx(AVFormatContext.streams,video_s_index);
       snprintf(@args[0], SizeOf(args),
               'video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d',
                AVCodecContext.width,AVCodecContext.height,AVCodecContext.pix_fmt,
                videoS.time_base.num, videoS.time_base.den,
                AVCodecContext.sample_aspect_ratio.num,
                AVCodecContext.sample_aspect_ratio.den);  
       ret := avfilter_graph_create_filter(@buffersrc_ctx,buffersrc,
                                           'in',@args[0], nil, filter_graph);
       if(ret < 0) then break;
       ret := avfilter_graph_create_filter(@buffersink_ctx, buffersink, 'out',
                                         nil, nil, filter_graph);
       if(ret < 0) then break;
       ret := av_opt_set_int_list(buffersink_ctx, 'pix_fmts', @pix_fmts[0],
                                  SizeOf(TAVPixelFormat),
                                  Int64(AV_PIX_FMT_NONE), AV_OPT_SEARCH_CHILDREN);
       if(ret < 0) then break;
       outputs.name       := av_strdup('in');
       outputs.filter_ctx := buffersrc_ctx;
       outputs.pad_idx    := 0;
       outputs.next       := nil;

       inputs.name       := av_strdup('out');
       inputs.filter_ctx := buffersink_ctx;
       inputs.pad_idx    := 0;
       inputs.next       := nil;
       filters_descr:= 'scale='+inttostr(FWidth)+':'+
                       inttostr(FHeight)+',fps=fps='+inttostr(FVideo_frame_rate);
       ret := avfilter_graph_parse_ptr(filter_graph, pchar(filters_descr),
                                @inputs, @outputs, nil); 
       if( ret < 0) then break;
       ret := avfilter_graph_config(filter_graph, nil);
       if(ret < 0) then  break;
       result := true;
    until 1 = 1;
  finally
     if assigned(inputs)  then avfilter_inout_free(@inputs);
     if assigned(outputs) then avfilter_inout_free(@outputs);
  end; 
    
end;

     过滤器使用起来简单而且功能强大。不同种类的过滤器作用不同,使用流程主要是这几步

   1.分配AVFilterGraph avfilter_graph_alloc()

   2.创建源过滤器 和 接收过滤器。

    int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt, const char *name,
       const char *args, void *opaque, AVFilterGraph *graph_ctx);

     通过name 'in'  或者 'out' 创建不同类的过滤器

   3.生成源和接收过滤器的输入输出  

    outputs.name       := av_strdup('in');

    ....

    inputs.name       := av_strdup('out');

    ....  

  4. 通过解析过滤器字符串添加过滤器

     avfilter_graph_parse_ptr(filter_graph, pchar(filters_descr), @inputs, @outputs, nil)

  5.检查过滤器的完整性

    avfilter_graph_config(filter_graph, nil);

  •   初始化SDL渲染对象
var
    FPreviewWnd:THandle; //视频预览窗体句柄
    SDL_Wnd:PSDL_Window; //根据FPreviewWnd创建的SDL窗体句柄
    SDL_Renderer:PSDL_Renderer;//渲染对象
    SDL_Texture:PSDL_Texture;
    FPreviewRect:TRect;        //视频预览的大小 width,height

function initializeSDLObj:boolean;
begin
  result : = false;
  repeat
    SDL_Wnd := SDL_CreateWindowFrom(Pointer(FPreviewWnd));
    if not assigned(SDL_Wnd) then break;
    SDL_Renderer := SDL_CreateRenderer(SDL_Wnd,-1,0);
    if not assigned(SDL_Renderer) then break;
    SDL_Texture := SDL_CreateTexture(SDL_Renderer,SDL_PIXELFORMAT_BGR24,
                                      SDL_TEXTUREACCESS_STREAMING,FWidth,FHeight); 
    if not assigned(SDL_Texture) then break;
    result : = true;
  until 1 = 1; 
end;
    

视频播放

  

//视频解码播放在线程中运行
var 
    Playhandle:THandle; 
    FQuitEvt:THandle;      //退出线程事件
    FCapturePicEvt:THandle; //拍照命令事件
    FCaptureNotify:THandle; //照片抓拍完成通知事件
    FCaptureData:TMemoryStream; //抓拍照片缓存
    CS_Capture:TRTLCriticalSection;//照片缓冲保护临界量

procedure OnCameraRenderer;
var
  iAVFrame,oAVFrame:PAVFrame;
  Events:TWOHandleArray;
  waitResult:longword;
  AVPacket:TAVPacket;
  avRet:integer;
  SDL_Rect:TSDL_Rect;
begin
    iAVFrame := AV_Frame_Alloc;
    oAVFrame := AV_Frame_Alloc;
    try
       iAVFrame.format := Ord(AVCodecContext^.pix_fmt);
       iAVFrame.width  := AVCodecContext^.width;
       iAVFrame.height := AVCodecContext^.height;
       Events[0] := FQuitEvt;
       Events[1] := FCapturePicEvt;
      repeat
         waitResult:=WaitForMultipleObjects(2,@Events,False,0);
         DoCapture := False;
         If waitResult = WAIT_OBJECT_0 Then Break; //接收到退出视频解码播放事件
         If waitResult = WAIT_OBJECT_0+1 Then
         Begin   //接收到拍照事件
           ResetEvent(FCapturePicEvt);
           DoCapture:=True;
         End;
         av_init_packet(@AVPacket);
         try
            avRet := av_read_frame(AVFormatContext,@AVPacket);
            if(avRet<0) then  break;
            if avRet = 0 then
            Begin
               if AVPacket.stream_index <> video_s_index then continue;
               //开始解码
               avRet := avcodec_send_packet(AVCodecContext,@AVPacket);
               if(avRet < 0) then
               Begin
                  if (avRet = AVERROR_EAGAIN) or (avRet = AVERROR_EOF) Then continue;
                  break;//解码失败退出
               End;

              while true do
              Begin
                  //获取解码后的数据
                  avRet := avcodec_receive_frame(AVCodecContext,iAVFrame);
                  if(avRet < 0) then
                  Begin
                    if (avRet = AVERROR_EAGAIN) or (avRet = AVERROR_EOF) Then break;
                    Exit;
                  End;
                 //视频数据转为BGR24
                 avRet :=   av_buffersrc_add_frame_flags(buffersrc_ctx,
                                       iAVFrame,AV_BUFFERSRC_FLAG_KEEP_REF);
                 if(avRet < 0) then  Exit;//过滤器转换失败
                 repeat
                    avRet := av_buffersink_get_frame(buffersink_ctx, oAVFrame);
                    try
                       if(avRet<0) then
                       Begin
                           if (avRet = AVERROR_EAGAIN) or 
                              (avRet = AVERROR_EOF) Then  break;
                           Exit;//转码失败
                       End;

                    if DoCapture Then //抓拍照片
                    Begin
                      EnterCriticalSection(CS_Capture);
                      try
           
                         FCaptureData.Clear;
                         FCaptureData.Write(oAVFrame^.linesize[0],sizeof(Integer));
                         FCaptureData.Write(oAVFrame^.width,sizeof(Integer));
                         FCaptureData.Write(oAVFrame^.height,sizeof(Integer));
                         FCaptureData.Write(OAVFrame^.data[0]^,
                                            oAVFrame^.linesize[0]*oAVFrame^.height);
                         SetEvent(FCaptureNotify);
                      finally
                        DoCapture := False;
                        LeaveCriticalSection(CS_Capture);
                      End;
                    End;

                    //渲染视频到指定窗体上
                    SDL_Rect.x:=FPreviewRect.Left;
                    SDL_Rect.y:=FPreviewRect.Top;
                    SDL_Rect.w:=FPreviewRect.Right-FPreviewRect.Left;
                    SDL_Rect.h:=FPreviewRect.Bottom-FPreviewRect.Top;
                    SDL_UpdateTexture(SDL_Texture,nil,
                                     Pbyte(oAVFrame^.data[0]),oAVFrame^.linesize[0]);
                    SDL_RenderClear(SDL_Renderer);
                    SDL_RenderCopy(SDL_Renderer, SDL_Texture, nil,@SDL_Rect);
                    SDL_RenderPresent(SDL_Renderer);
                  finally
                    av_frame_unref(oAVFrame); //释放缓存
                  end;
              until 1 = 2;
            End;
         End;
      finally
        av_packet_unref(@AVPacket);//释放缓存
      end;
    until 1 = 2;
  finally
    av_frame_free(@iAVFrame);
    av_frame_free(@oAVFrame);
  End;
end;

视频播放在另启一个线程运行。 CreateThread();

拍照

上面的方法函数OnCameraRenderer。中接收拍照命令事件,完成抓拍图片并保存到缓存数据FCaptureData。 缓存数据包含。lineSize.图片的每行字节数。Width 图片的宽度 Height 图片的高度   图片的BGR24数据。 通过函数 BGR24ToBitmap() 转成bmp图片。

function BGR24ToBitmap(const frame: Pbyte; const linesize,
  width, height: integer):TBitmap;
var
  P:PByte;
  I:integer;
begin
  Result:=TBitmap.Create;
  Result.Width:=Width;
  Result.Height:=Height;
  Result.PixelFormat:=pf24bit;
  P:=frame;
  for I:=0 To Result.Height-1 Do
  Begin
     Move(P^,Result.ScanLine[I]^,linesize);
     Inc(P,linesize);
  End;
end;

参数frame 为图片BGR24格式的数据指针。

Demo下载

上面的代码实现的主要源代码。具体的实现Demo 重这里下载

usbCamera.rar-编解码文档类资源-CSDN下载

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红锅巴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值