老陈---Usb摄像头专题讲座


文档作者:陈经韬

本文主要讲述视频数据获取、保存为mpeg、调用Mpeg4压缩算法、自己用Delphi写编解码器和如何防范Usb偷窥。

一:获取摄像头数据

    获取数据可以使用Directx或Vfw接口。一般来说,Directx比较占用cpu,而且com接口是比较麻烦的,所以一般使用vfw。不过,如果想直接捕获视频和声音保存为wmv文件,那么就要使用Directx。我们这里先讲vfw的。

1:Vcl法:到网上搜索VideoCap控件,拖放到窗口即可。
2:API法:网上已经有很多相关介绍了,这里还是重复一下:
下面给出一个简单的例子,主要完成数据捕获和压缩。同时为了趣味性,还加上字幕功能。
添加单元vfw.pas,同时本例子还用到jpeg压缩,所以还要添加jpeg单元。完整代码如下(注意:代码没有做过多容错处理,请自行完善):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, {} vfw, Jpeg, {} Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TFrmMain = class(TForm)
    Button1: TButton;
    Panel1: TPanel;
    Image1: TImage;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    CaptureHandle: THandle;
    BmpInfo: TBitmapInfo;
    procedure CompareFrame(lpVHdr: PVIDEOHDR);
  public
    { Public declarations }
  end;

var
  FrmMain: TFrmMain;


implementation

{$R *.dfm}

procedure TFrmMain.FormCreate(Sender: TObject);
begin
  CaptureHandle := 0;
end;

procedure GetUsbCamerBmpSize(var BmpInfoHeader: TBitmapinfoheader);
var
  PBmpInfoHeader: PBitmapInfo;
  dwSize: DWORD;
begin
  dwSize := capGetVideoFormatSize(FrmMain.CaptureHandle);
  PBmpInfoHeader := GlobalAllocPtr(GHND, dwSize);
  capGetVideoFormat(FrmMain.CaptureHandle, PBmpInfoHeader, dwSize);
  CopyMemory(@BmpInfoHeader, @PBmpInfoHeader.bmiHeader, Sizeof(TBitmapinfoheader));
  GlobalFreePtr(PBmpInfoHeader);
end;

procedure TFrmMain.CompareFrame(lpVHdr: PVIDEOHDR);
var
  BmpFileHeader: TBitmapFileHeader;
  BmpInfoHeader: TBitmapInfoHeader;
  MyMemoryStream: TMemoryStream;
  MyBmp: TBitmap;
  MyJpg: TJPEGImage;
begin
{注意:实际上,lpVHdr里面已经包含有图像的裸数据了.可以直接Draw显示出来了.
这里因为需要添加字幕,同时转化为Jpeg格式.所以我们为裸数据加上Bmp文件头和
结构.
}
  FillChar(BmpFileHeader, Sizeof(TBitmapfileheader), 0);
  FillChar(BmpInfoHeader, Sizeof(TBitmapinfoheader), 0);

  BmpFileHeader.bfType := $4D42; //文件类型,必须为BM.
  BmpFileHeader.bfSize := BmpInfo.bmiHeader.biSizeImage; //BMP数据的大小字节
  BmpFileHeader.bfReserved1 := 0; //保留,必需为0
  BmpFileHeader.bfReserved2 := 0; //保留,必需为0
  BmpFileHeader.bfOffBits := Sizeof(TBitmapFileHeader) + Sizeof(TBitmapInfoHeader); //Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits.

  GetUsbCamerBmpSize(BmpInfoHeader);


  Panel1.Left := 0;
  Panel1.Top := 0;
  Panel1.ClientWidth := BmpInfoHeader.biWidth;
  Panel1.ClientHeight := BmpInfoHeader.biHeight;


  MyBmp := TBitmap.Create;
  MyJpg := TJPEGImage.Create;
  MyMemoryStream := TMemoryStream.Create;

  MyMemoryStream.Write(BmpFileHeader, sizeof(BmpFileHeader));
  MyMemoryStream.Write(BmpInfoHeader, sizeof(BmpInfoHeader));
  MyMemoryStream.Write(lpVHdr^.lpData^, BmpInfo.bmiHeader.biSizeImage);
  MyMemoryStream.Position := 0;

  MyBmp.LoadFromStream(MyMemoryStream);

  with MyBmp.Canvas do
  begin
    Brush.style := bsClear; //先这样设置
    Font.Color := clRed; // 文字前景色
    Font.Size := 20; //TxtFont.Size;//10;//Self.Font.Size;
//Font.Name := Self.Font.Name;
    TextOut(0, 0, DateTimeToStr(Now)); //else
//if RadioButton3.Checked then TextOut(0,0,Edit1.Text);
  end;


  Image1.Picture.Bitmap.Assign(MyBmp);
  MyJpg.Assign(MyBmp);
  MyJpg.CompressionQuality := 65;
  MyMemoryStream.Clear;
  MyJpg.SaveToStream(MyMemoryStream);
  MyMemoryStream.Position := 0;
//SendVideoBufToClient(MyMemoryStream);//发送数据出去
  MyMemoryStream.Free;
  MyBmp.Free;
  MyJpg.Free;
end;

function FrameCallBack(hWnd: HWND; lpVHdr: PVIDEOHDR): DWORD; stdcall;
begin
  FrmMain.CompareFrame(lpVHdr);
  Result := DWORD(True);
end;

procedure TFrmMain.Button1Click(Sender: TObject);
var
  CapParms: TCAPTUREPARMS;
begin
  //定义视频输入格式
  FillChar(BmpInfo.bmiHeader, SizeOf(TBitmapInfoHeader), 0);
  with BmpInfo.bmiHeader do
  begin
    biBitCount := 24;
    biClrImportant := 0;
    biClrUsed := 0;
    biCompression := BI_RGB;
    biHeight := 240;
    biPlanes := 1;
    biSize := SizeOf(TBitmapInfoHeader);
    biSizeImage := 0;
    biWidth := 320;
    biXPelsPerMeter := 0;
    biYPelsPerMeter := 0;
  end;

  CaptureHandle := capCreateCaptureWindow('Capture Window',
    WS_VISIBLE or WS_CHILD, 0, 0, 320, 240, Handle, 0); //创建一个AVICap捕获窗口
  if CaptureHandle = 0 then
  begin
    ShowMessage('创建窗口失败!');
    Exit;
  end;
  if not capDriverConnect(CaptureHandle, 0) then //连接摄像头.0代表第一个摄像头
  begin
    ShowMessage('打开摄像头失败!');
    Exit;
  end;
  capSetVideoFormat(CaptureHandle, @BmpInfo, SizeOf(BmpInfo)); //设置视频格式
  capPreviewRate(CaptureHandle, 15); //设置预览视频的频率
  capSetCallbackOnVideoStream(CaptureHandle, @FrameCallBack); //设置回调函数.流格式.
  //capSetCallbackOnFrame(CaptureHandle, @FrameCallBack);//帧格式
  capCaptureGetSetup(CaptureHandle, @CapParms, sizeof(TCAPTUREPARMS)); //获取当前设置
  CapParms.fYield := TRUE;
  CapParms.fAbortLeftMouse := FALSE;
  CapParms.fAbortRightMouse := FALSE;
  capCaptureSetSetup(CaptureHandle, @CapParms, sizeof(TCAPTUREPARMS)); //改变需要改变的参数
  capCaptureSequenceNoFile(CaptureHandle); //不保存文件
end;

 

procedure TFrmMain.Button2Click(Sender: TObject);
begin
  if CaptureHandle <> 0 then
  begin
    CapCaptureStop(CaptureHandle); //停止捕获
//capSetCallbackOnFrame(CaptureHandle,nil);
    capDriverDisconnect(CaptureHandle); //断开连接
  end;
end;


end.

二:发送和保存

    现在我们简单修改一下第一章节的程序,让它可以发送捕获的数据,接收端可以保存为mpeg文件.为了方便,网络部分我们直接使用Delphi自带的Indy.数据保存部分,可以使用Directx接口.我们这里使用了一个mpeg的代码.购买该代码可以打开http://www.msbsoftware.it/mpegpas/.我们来看修改后的发送端代码.

unit Unit_Send;

interface

uses
  Windows, Messages, SysUtils, {} vfw, Jpeg, {} Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, IdAntiFreezeBase, IdAntiFreeze,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient;

type
  TFrmMain = class(TForm)
    btnStart: TButton;
    Panel1: TPanel;
    Image1: TImage;
    btnStop: TButton;
    Label1: TLabel;
    Edit1: TEdit;
    IdTCPClient1: TIdTCPClient;
    IdAntiFreeze1: TIdAntiFreeze;
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    CaptureHandle: THandle;
    BmpInfo: TBitmapInfo;
    procedure CompareFrame(lpVHdr: PVIDEOHDR);
  public
    { Public declarations }
  end;

var
  FrmMain: TFrmMain;


implementation

{$R *.dfm}

procedure TFrmMain.FormCreate(Sender: TObject);
begin
  CaptureHandle := 0;
  btnStart.Enabled := True;
  btnStop.Enabled := False;
end;

procedure GetUsbCamerBmpSize(var BmpInfoHeader: TBitmapinfoheader);
var
  PBmpInfoHeader: PBitmapInfo;
  dwSize: DWORD;
begin
  dwSize := capGetVideoFormatSize(FrmMain.CaptureHandle);
  PBmpInfoHeader := GlobalAllocPtr(GHND, dwSize);
  capGetVideoFormat(FrmMain.CaptureHandle, PBmpInfoHeader, dwSize);
  CopyMemory(@BmpInfoHeader, @PBmpInfoHeader.bmiHeader, Sizeof(TBitmapinfoheader));
  GlobalFreePtr(PBmpInfoHeader);
end;

function ChangeBmp(var MyBmp: TBitmap): Boolean; {动态改变BMP图像大小}
var
  TempBitmap: TBitmap;
begin
  TempBitmap := TBitmap.Create;
  TempBitmap.Assign(MyBmp);
  MyBmp.Width := 160; //176
  MyBmp.Height := 120; //144
  MyBmp.PixelFormat := pf15bit;
  SetStretchBltMode(MyBmp.Canvas.Handle, COLORONCOLOR);
  stretchblt(MyBmp.Canvas.Handle, 0, 0, MyBmp.Width, MyBmp.Height, TempBitmap.Canvas.Handle, 0, 0, TempBitmap.Width, TempBitmap.Height, srccopy);
  TempBitmap.Free;
  Result := True;
end;

procedure TFrmMain.CompareFrame(lpVHdr: PVIDEOHDR);
var
  BmpFileHeader: TBitmapFileHeader;
  BmpInfoHeader: TBitmapInfoHeader;
  MyMemoryStream: TMemoryStream;
  MyBmp: TBitmap;
  MyJpg: TJPEGImage;
begin
{注意:实际上,lpVHdr里面已经包含有图像的裸数据了.可以直接Draw显示出来了.
这里因为需要添加字幕,同时转化为Jpeg格式.所以我们为裸数据加上Bmp文件头和
结构.
}
  FillChar(BmpFileHeader, Sizeof(TBitmapfileheader), 0);
  FillChar(BmpInfoHeader, Sizeof(TBitmapinfoheader), 0);

  BmpFileHeader.bfType := $4D42; //文件类型,必须为BM.
  BmpFileHeader.bfSize := BmpInfo.bmiHeader.biSizeImage; //BMP数据的大小字节
  BmpFileHeader.bfReserved1 := 0; //保留,必需为0
  BmpFileHeader.bfReserved2 := 0; //保留,必需为0
  BmpFileHeader.bfOffBits := Sizeof(TBitmapFileHeader) + Sizeof(TBitmapInfoHeader); //Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits.

  GetUsbCamerBmpSize(BmpInfoHeader);


  Panel1.Left := 0;
  Panel1.Top := 0;
  Panel1.ClientWidth := BmpInfoHeader.biWidth;
  Panel1.ClientHeight := BmpInfoHeader.biHeight;


  MyBmp := TBitmap.Create;
  MyJpg := TJPEGImage.Create;
  MyMemoryStream := TMemoryStream.Create;

  MyMemoryStream.Write(BmpFileHeader, sizeof(BmpFileHeader));
  MyMemoryStream.Write(BmpInfoHeader, sizeof(BmpInfoHeader));
  MyMemoryStream.Write(lpVHdr^.lpData^, BmpInfo.bmiHeader.biSizeImage);
  MyMemoryStream.Position := 0;

  MyBmp.LoadFromStream(MyMemoryStream);

  with MyBmp.Canvas do
  begin
    Brush.style := bsClear; //先这样设置
    Font.Color := clRed; // 文字前景色
    Font.Size := 20; //TxtFont.Size;//10;//Self.Font.Size;
//Font.Name := Self.Font.Name;
    TextOut(0, 0, DateTimeToStr(Now)); //else
//if RadioButton3.Checked then TextOut(0,0,Edit1.Text);
  end;

  ChangeBmp(MyBmp); //因为接收方的mpeg固定为此大小.所以必须改变它.当然,也可以先发送大小过去动态设置,则可省略此步.
  Image1.Picture.Bitmap.Assign(MyBmp);
  MyJpg.Assign(MyBmp);
  MyJpg.CompressionQuality := 65;
  MyMemoryStream.Clear;
  MyJpg.SaveToStream(MyMemoryStream);
  MyMemoryStream.Position := 0;

  //发送数据出去
  try
    IdTCPClient1.WriteInteger(MyMemoryStream.Size);
    IdTCPClient1.WriteStream(MyMemoryStream);
  except
    btnStop.Click;
    MyMemoryStream.Free;
    MyBmp.Free;
    MyJpg.Free;
    ShowMessage('发送失败');
    Exit;
  end;

  MyMemoryStream.Free;
  MyBmp.Free;
  MyJpg.Free;
end;

function FrameCallBack(hWnd: HWND; lpVHdr: PVIDEOHDR): DWORD; stdcall;
begin
  FrmMain.CompareFrame(lpVHdr);
  Result := DWORD(True);
end;

procedure TFrmMain.btnStartClick(Sender: TObject);
var
  CapParms: TCAPTUREPARMS;
begin
  (Sender as TButton).Enabled := False;
  IdTCPClient1.Host := Trim(Edit1.Text);
  IdTCPClient1.Port := 2000;

  try
    IdTCPClient1.Connect(1000 * 10);
  except
    ShowMessage('连接失败!');
    (Sender as TButton).Enabled := True;
    Exit;
  end;

  //定义视频输入格式
  FillChar(BmpInfo.bmiHeader, SizeOf(TBitmapInfoHeader), 0);
  with BmpInfo.bmiHeader do
  begin
    biBitCount := 24;
    biClrImportant := 0;
    biClrUsed := 0;
    biCompression := BI_RGB;
    biHeight := 240;
    biPlanes := 1;
    biSize := SizeOf(TBitmapInfoHeader);
    biSizeImage := 0;
    biWidth := 320;
    biXPelsPerMeter := 0;
    biYPelsPerMeter := 0;
  end;

  CaptureHandle := capCreateCaptureWindow('Capture Window',
    WS_VISIBLE or WS_CHILD, 0, 0, 320, 240, Handle, 0); //创建一个AVICap捕获窗口
  if CaptureHandle = 0 then
  begin
    ShowMessage('创建窗口失败!');
    (Sender as TButton).Enabled := True;
    Exit;
  end;
  if not capDriverConnect(CaptureHandle, 0) then //连接摄像头.0代表第一个摄像头
  begin
    ShowMessage('打开摄像头失败!');
    (Sender as TButton).Enabled := True;
    Exit;
  end;
  capSetVideoFormat(CaptureHandle, @BmpInfo, SizeOf(BmpInfo)); //设置视频格式
  capPreviewRate(CaptureHandle, 15); //设置预览视频的频率
  capSetCallbackOnVideoStream(CaptureHandle, @FrameCallBack); //设置回调函数.流格式.
  //capSetCallbackOnFrame(CaptureHandle, @FrameCallBack);//帧格式
  capCaptureGetSetup(CaptureHandle, @CapParms, sizeof(TCAPTUREPARMS)); //获取当前设置
  CapParms.fYield := TRUE;
  CapParms.fAbortLeftMouse := FALSE;
  CapParms.fAbortRightMouse := FALSE;
  capCaptureSetSetup(CaptureHandle, @CapParms, sizeof(TCAPTUREPARMS)); //改变需要改变的参数
  capCaptureSequenceNoFile(CaptureHandle); //不保存文件
  btnStop.Enabled := True;
end;

 

procedure TFrmMain.btnStopClick(Sender: TObject);
begin
  (Sender as TButton).Enabled := False;
  if CaptureHandle <> 0 then
  begin
    CapCaptureStop(CaptureHandle); //停止捕获
//capSetCallbackOnFrame(CaptureHandle,nil);
    capDriverDisconnect(CaptureHandle); //断开连接
  end;
  if IdTCPClient1.Connected then IdTCPClient1.Disconnect;
end;


end.

 

接收端代码:

unit Unit_Recv;

interface

uses
  Windows, Messages, SysUtils, {} mpeg, jpeg, {} Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPServer, IdThreadMgr,
  IdThreadMgrDefault, IdAntiFreezeBase, IdAntiFreeze, ExtCtrls, StdCtrls;

type
  TFrmMain = class(TForm)
    IdTCPServer1: TIdTCPServer;
    btnStart: TButton;
    CheckBox1: TCheckBox;
    Panel3: TPanel;
    Image1: TImage;
    IdAntiFreeze1: TIdAntiFreeze;
    IdThreadMgrDefault1: TIdThreadMgrDefault;
    btnStop: TButton;
    procedure btnStartClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure IdTCPServer1Execute(AThread: TIdPeerThread);
  private
    { Private declarations }
    MyMpeg: TMpeg;
    fs: TFileStream;
  public
    { Public declarations }
  end;

var
  FrmMain: TFrmMain;

implementation

{$R *.dfm}

procedure TFrmMain.FormCreate(Sender: TObject);
begin
  CheckBox1.Checked := True;
  btnStart.Enabled := True;
  btnStop.Enabled := False;
  MyMpeg := nil;
  fs := nil;
end;

procedure TFrmMain.btnStartClick(Sender: TObject);
var
  m_FileName: string;
begin
  (Sender as TButton).Enabled := False;
  try
    IdTCPServer1.DefaultPort := 2000;
    IdTCPServer1.Active := True;
  except
    ShowMessage('打开监听端口失败!');
    (Sender as TButton).Enabled := True;
    Exit;
  end;
  CheckBox1.Enabled := False;
  if CheckBox1.Checked then
  begin
    m_FileName := ExtractFilePath(Application.ExeName) + 'Demo.mpeg';
    MyMpeg := TMpeg.Create;
    fs := TFileStream.Create(m_FileName, fmCreate or fmOpenReadWrite or fmShareDenyNone);
    MyMpeg.Open(160, 120, 4, 2000, bf24hz, fs);
  end;
  btnStop.Enabled := True;
end;

procedure TFrmMain.btnStopClick(Sender: TObject);
begin
  (Sender as TButton).Enabled := False;
  if MyMpeg <> nil then MyMpeg.Free;
  if fs <> nil then fs.Free;
end;

procedure TFrmMain.IdTCPServer1Execute(AThread: TIdPeerThread);
var
  iSize: integer;
  MyStream: TMemoryStream;
  MyBmp: TBitmap;
  MyJpg: TJPEGImage;
begin
  try
    iSize := AThread.Connection.ReadInteger;
  except
    Exit;
  end;
  MyStream := TMemoryStream.Create;
  try
    AThread.Connection.ReadStream(MyStream, iSize);
  except
    MyStream.Free;
    Exit;
  end;
  MyStream.Position := 0;
  MyBmp := TBitmap.Create;
  MyJpg := TJPEGImage.Create;
  MyJpg.LoadFromStream(MyStream);
  MyBmp.Assign(MyJpg);
  if CheckBox1.Checked then
  begin
    MyMpeg.AddIImage(MyBmp);
    MyMpeg.Keep(2);
  end;
  Image1.Picture.Bitmap.Assign(MyBmp);
  MyStream.Free;
  MyJpg.Free;
  MyBmp.Free;
end;

end.

完整代码点这里下载.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值