分享
 
 
 

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

王朝delphi·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

文章分类:网络、视频,聊天

文章作者:廖长科

(转贴请注明原创作者)

笔者序:也许在写这编文章时,有很多朋友正被老板要求做类似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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有