WEB开发网
开发学院软件开发Delphi 在网络上进行摄像头视频通讯 阅读

在网络上进行摄像头视频通讯

 2006-02-04 13:57:08 来源:WEB开发网   
核心提示: 文章分类:网络、视频,聊天 文章作者:廖长科 (转贴请注明原创作者) 笔者序:也许在写这编文章时,在网络上进行摄像头视频通讯,有很多朋友正被老板要求做类似QQ一样的视频聊天软件,在这里,想一想DirectShow的功能真是强大哟,哈哈,我把自己的一些经验和代码写出来与大家一起分享,高手不要笑我哈!看了这编文章

   文章分类:网络、视频,聊天
  
文章作者:廖长科
   (转贴请注明原创作者)

   笔者序:也许在写这编文章时,有很多朋友正被老板要求做类似QQ一样的视频聊天软件,在这里,我把自己的一些经验和代码写出来与大家一起分享,高手不要笑我哈!看了这编文章后,你也可以自己做一个简单的网络视频通讯软件,如果自己家里上了网,就可以在公司和家人进行可视通讯了,多爽,不用给电话费了。

    本例子使用的是简的老技术(VFW),开发起来相对简单,以下是Delphi代码,你需要先加入VFW.PAS文件,没有这个文件你可以在网上找一下。作者从Delphi4就开始编程,其实Delphi可以做很多事情,只是太多Delphi程序员没有深专技术和思想,没有超越自己,Delphi只是一个开发工具,代码思想是的设计的精髓。
    下面让我们一起来讲解一下:

    在程序的开始,你需要用capCreateCaptureWindow来创建一个摄像头句柄,
    CapWnd := capCreateCaptureWindow('预览窗口',WS_VISIBLE or WS_CHILD,0,0,320,240,PRevWnd,1);
    在后面的参数:PrevWnd代表预览窗口的句柄,你可以指定一个Panel的句柄;320和240代表了窗口的长宽。
  
    if CapWnd = 0 then exit;
    capDriverConnect(CapWnd,0);  //连接摄像头设备

    capDlgVideoFormat(CapWnd);  //显示视频设置对话框,进行配置视频的大小、颜色位数等。
    capGetVideoFormat(CapWnd,@BmpInInfo,sizeof(BITMAPINFO));  //取得视频图像数据头,后面压缩时需要用到

    capPreviewRate(CapWnd, 33);  //设置预览视频的频率,33代表第秒30帧。
    capPreview(CapWnd, TRUE);

    capSetCallbackOnFrame(CapWnd,FrameCallBack); 
   
    InitCaptureParams;
   
    最后一句是设置视频压缩参数, 后面会进行说明。其中的capSetCallbackOnFrame(CapWnd,FrameCallBack)是设置每帧视频数据的回调函数,我们就可以将回调时的视频数据通过网络进行传输,这样的就实现了视频聊天的核心了。

   回调函数如下的格式:

   function  FrameCallBack(hWnd: HWND; lpVHdr: PVIDEOHDR): DWord; stdcall;
   var
    bKeyFrame : BOOL ;
    Buf : PBYTE;
    VideoData : TVIDEO_DATA;
    OutActSize : dword;
    i : integer;
   begin
    OutActSize := BmpInInfo.bmiHeader.biSizeImage;
    Buf := ICSeqCompressFrame(@CapVar,0,lpVHdr.lpData,@bKeyFrame,@OutActSize);
   
    //在这里, OutActSize代表压缩后的视频数据大小
    //  form1.Label3.Caption := 'Compressed size:'+inttostr(OutActSize);


    //我用的是UDP方式, 因为UDP数据包大小限制, 所以我控制了数据大小, 超出的数据会发生丢帧
    if (OutActSize <= sizeof(videodata.Buf) ) then
    begin
     zeromemory(@VideoData ,sizeof(TVIDEO_DATA));
    
     //记录是否为关键帧
     VideoData.bKeyFrame:=bKeyFrame;


     copymemory(@VideoData.Buf, Buf, OutActSize);
    
     VideoData.SampleNum:=SampleNum; //我们可以记录下帧数, 可以做扩展用
     VideoData.BufSize:=OutActSize;  //记录数据大小, 传输时用
    
     //在这里, 你可以用你喜欢的网络方式传输视频数据,
    
     //cc1.SendBuffer(VideoData,sizeof(TVIDEO_DATA)-SendBufferSize+Outactsize);

     inc(SampleNum);
    end;   
    result := 0;
   end;

   其中,PVIDEOHDR类型可以从VFW中看到其定义:
   TVIDEOHDR        = record
     lpData        : PBYTE;  // 视频数据buffer
     dwBufferLength    : DWORD;  // 数据buffer长度
     dwBytesUsed     : DWORD;        
     dwTimeCaptured    : DWORD;  // 时间长度(毫秒)
     dwUser        : DWORD;        
     dwFlags       : DWORD;        
     dwReserved      : array[0..3] of DWORD;
   end;
  
   在回调函数中, 只用到了视频函数: ICSeqCompressFrame,可以看到此函数传入了CapVar参数,这个参数是由我们先前看到的InitCaptureParams函数产生,下面代码来实现:
   function InitCaptureParams : boolean;
   begin
    result := False;
 
    //初始化CapVar
    zeromemory(@CapVar,sizeof(TCOMPVARS));

    CapVar.cbSize:=sizeof(CapVar); //必须指定cbSize为TCOMPVARS结构大小
    CapVar.dwFlags:=ICMF_COMPVARS_VALID;

    CapVar.cbState:=0;
 
    //fccHandler代表压缩编码类型,我们使用的是DIVX的编码器
    CapVar.fccHandler:=mmioFOURCC('d','i','v','x');
    CapVar.fccType:=ICTYPE_VIDEO;

   
    //正式连接编码器
    CapVar.hic:=ICOpen(ICTYPE_VIDEO, CapVar.fccHandler, ICMODE_COMPRESS);
  
    if (CapVar.hic>0) then
    begin

     OutFormatSize:=ICCompressGetFormatSize(CapVar.hic,@BmpInInfo.bmiHeader);
     getmem(BmpOutInfo,OutFormatSize);
    
     //我们可以通过初始化时得到的BmpInInfo来获取压缩传出图像头BmpOutInfo
     ICCompressGetFormat(CapVar.hic,@BmpInInfo.bmiHeader,@BmpOutInfo^.bmiHeader);
     OutBufferSize:=ICCompressGetSize(CapVar.hic,@BmpInInfo.bmiHeader,@BmpOutInfo^.bmiHeader);
     ICSeqCompressFrameStart(@CapVar, @BmpInInfo);
     result := True;
    end
    else
    begin
     ShowMsg('请先安装视频压缩编码器');
     Exit;
    end
   end;

   使用之后,如果要断开编码器连接,是这样调用的:
   if (CapVar.hic > 0) then
   begin
    ICSeqCompressFrameEnd(@CapVar);
    ICCompressorFree(@CapVar);
    ICClose(CapVar.hic);
   end;

   于是,服务端的摄像头数据捕捉连接就完成了,那么对于客户端是乍样进行视频数据解压呢?这个问题当然还是通过IC函数解决,但你必须先把服务端上的BmpOutinfo和CapVar传输到客户端才行。
  
   接着,一起来看看客户端的图像显示过程:
   //先用取得的CapVar来连接视频编码器
   CapVar.hic := ICOpen(CapVar.fccType,CapVar.fccHandler,ICMODE_DECOMPRESS);
  
   //成功后,用服务器传来的BmpOutInfo当作客户端的BmpInInfo来取得解压输出的图像头BmpOutInfo

   OutFormatSize:=ICDecompressGetFormatSize(CapVar.hic,@BmpInInfo.bmiHeader);
   GetMem(BmpOutInfo,OutFormatSize);
   zeromemory(BmpOutInfo,OutFormatSize);

   ICDecompressGetFormat(CapVar.hic, @BmpInInfo.bmiHeader, @BmpOutInfo^.bmiHeader);

   OutBufferSize:=BmpOutInfo^.bmiHeader.biSizeImage;
   getmem(OutBuffer,OutBufferSize);

   zeromemory(OutBuffer,OutBufferSize);
   ICDecompressBegin(CapVar.hic,@BmpInInfo.bmiHeader, @BmpOutInfo^.bmiHeader);


   最后,当然是视频数据的解压过程

   if VIDEO_DATA.bKeyFrame then
    Result:=ICDecompress(CapVar.hic,0,@BmpInInfo,@VIDEO_DATA.Buf,
          @BmpOutInfo.bmiHeader,OutBuffer)
   else
    Result:=ICDecompress(CapVar.hic,ICDECOMPRESS_NOTKEYFRAME,@BmpInInfo,@VIDEO_DATA.Buf,
          @BmpOutInfo.bmiHeader,OutBuffer);
   if (Result=ICERR_OK) then
   begin
    SetDIBitsToDevice(Canvas.Handle,0,0,bmptmp.Width,bmptmp.Height,0,0,0,BmpOutInfo^.bmiHeader.biHeight ,
          OutBuffer,BmpOutInfo^,DIB_RGB_COLORS);
   end;

   这样,传送过来的视频数据变直接画到了Canvas.Handle上了。
   还忘记了服务端关闭摄像头的方法,调用capDriverDisconnect(CapWnd) 就OK了。

   全文就Over了,jasonke还要说的就是,这个方法是用的微软的老函数,不过实现起来很简单,相信会点API的都能开发出来,还有一种方法当然是用DirectShow了哟,这需要你开发Filter,要搞明白微软的几个接口,你可以看看DShowNetwork例子。这个方法也有很多C++的兄弟在痛苦的实现,想一想DirectShow的功能真是强大哟,哈哈。

欢迎大家光临《黑雨共享软件》,谢谢大家对中国共享软件的支持

http://brsoft.0033.cn 
http://brsoft.008.net

Tags:网络 进行 摄像头

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接