打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Delphi中控制、使用摄像头
笔 者序:也许在写这编文章时,有很多朋友正被老板要求做类似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例子。 
网中戏 发表于 2006-7-27 12:25:42
廖长科 在关键的回调函数一笔带过、不过可以参考

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;

原文来自: http://www.cnblogs.com/python001/ 【100脚本网


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
VFW + vcm
Directshow实现电影纹理
VC++关于的VFW视频采集方案(续)
VC++6.0将数据文件图像化
AGG 渲染缓存(Rendering Buffer)
Image Processing Using GDI+
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服