2024年2月21日发(作者:)
用Delphi实现网络视频编
在MSN、QQ等聊天类的应用程序中,都应用到了网络视频技术。Delphi使用Object Pascal语言是一种完全面向对象语言,可以开发出灵活强大的程序,开发网络视频程序也不在话下。一个完整的网络视频程序应包括以下几个关键技术:视频捕获、视频压缩与解压、数据传输。
一、视频获捕
1.基本概念
微软为软件开发人员提供了一个专门用于视频捕获的VFW (Video for Windows) SDK,为在Windows系统中实现视频捕获提供了标准的接口,从而大大方便了视频捕获程序的开发。由于VFW SDK只有VC和VB版,没有Delphi版,因此需要在Delphi中重新声明DLL中的各个函数和变量(可以参考MSDN中的VC的函数声明以及变量定义,也可以从网上下载写好的头文件)。
VFW是Microsoft 1992年推出的关于数字视频的一个软件包,它能使应用程序数字化并播放从传统模拟视频源得到的视频剪辑。VFW的一个关键思想是播放时不需要专用硬件,为了解决数字视频数据量大的问题,需要对数据进行压缩。它引进了一种叫AVI的文件标准,该标准未规定如何对视频进行捕获、压缩及播放,仅规定视频和音频该如何存储在硬盘上,在AVI文件中交替存储视频帧和与之相匹配的音频数据。VFW给程序员提供VBX和AVICap窗口类的高级编程工具,使程序员能通过发送消息或设置属性来捕获、播放和编辑视频剪辑。
2.AVICap编程
AVICap支持实时的视频流捕获和单帧捕获并提供对视频源的控制,它能直接访问视频缓冲区,不需要生成中间文件,实时性很强,效率很高。同时,它也可将数字视频捕获到文件。AVICap编程的基本步骤包括:
第一步,创建AVICap窗口。通过capCreateCaptureWindow函数创建一个捕获窗,所有的捕获操作及其设置都以它为基础。窗口风格一般为WS_CHILD和WS_VISIBLE。
第二步,设置AVICap窗口的相关属性。通过capPreviewRate函数设置视频捕获速率;通过capPreview函数或capOverlay函数设置显示视频时的模式(普通的摄像头不能用overlay的方式);通过capSetVideoFormat函数设置视频格式(包括长度、宽度等);通过capDlgVideoSource、capDlgVideoFormat、capDlgVideoCompression显示控制视频源、视频格式、视频压缩的对话框。
第三步,定义回调函数。定义捕获帧回调函数,获得每一帧的数据,并对每一帧的数据进行处理,比如压缩、传输到客户端等;定义窗口状态回调函数,获得窗口的状态;定义错误回调函数,获得并处理错误信息。
3.相关控件
虽然利用上面介绍的API可以实现视频捕获编程,但如果将这些API封装成一个控件则编程更为方便,这样的控件可以从常用Delphi网站找到。本文以TvideoCap控件为例,实现视频捕获。
(1)相关属性及方法
DriverIndex该属性是用来指定视频捕获设备序号,从0开始。
DriverOpen该属性是用来确定是否打开指定的视频捕获设备。设置为True表示打开,False表示关闭。
CapToFile该属性是用来确定捕获的同时是否将捕获的画面保存成AVI格式的视频文件。设置为True表示保存,False表示不保存。
VideoPreview该属性是确定捕获的同时是否预览。设置为True表示预览,False表示不预览。
StartCapture该方法是用来捕获视频数据,执行该方法后才会触发相关事件。
其他的属性和方法这里就不一一介绍。
(2)相关事件
OnVideoStream当捕获视频数据时触发该事件,在这里可以获得每一帧的数据,进行相关处理,发送到客户端。
二、视频压缩与解压
通过AVICap窗口捕获的每一帧的数据是以BMP(RAW)文件格式存放的,若直接进行传输,数据量非常大,对网络的带宽要求非常高,因此在传输之前必须对每一帧的数据进行压缩处理后再进行传输。具体步骤:
第一步,安装视频压缩引擎。媒体播放器软件都带有压缩引擎,也可以从网上下载单独的解压缩引擎,比如MPEG4或DIVX等。
第二步,初始化压缩引擎。选择压缩引擎,获得压缩引擎的支持,确定输入、输出格式,设置压缩器。
第三步,压缩帧数据。通过指定的压缩引擎,对获取的每一帧数据进行压缩。
解压的过程与压缩的过程类似,通过选择相对应的解压引擎,将压缩的数据解压,以便于回放。
三、数据传输
1.基本概念
计算机在传输数据时有两种方式:分别是TCP(Transmission Control Protocol,传输控制协议)及UDP(User Datagram Protocol,用户数据报协议),两者分别因数据传输的不同请求而提供不同的数据传输方式。
(1) TCP协议
TCP是一个基于连接的通信协议,主要目的是提供大量数据传输并确保其传输无误,因此提供错误检查、数据复原及数据重传等机制。TCP在传输数据之前,会先在主机间(例如主控端与被控端)创建连接。根据此连接,数据可在计算机间相互传输,即所谓的双向传输模式。
(2) UDP协议
UDP是一个非连接式的通信协议,主要目的在于传输少量的数据。与TCP不同的是,TCP在传输之前必须创建连接,而UDP不需要,只要设置计算机间的IP及使用相同的端口,就可以相互传输数据。因此UDP只提供单向的数据传输,即所谓的单向无连接传输模式。
由于UDP不需要先创建连接,节省了TCP创建连接所需的时间,所以适合在主机间进行单向的数据传输。由于视频数据的传输对于实时性要求很高,即使传输过程中有个别帧的数据有错,也不会影响整个视频的效果,故本文将会详细介绍如何通过UDP实现视频数据的传输。
2.控件及相关内容介绍
在Delphi中对于UDP及TCP都提供了很好的支持,而且将它们封装起来。开发人员无须知道协议的具体实现细节,而只要使用Delphi提供的TIdUDPServer元件(在Indy Servers页)即可完成相应的功能。下面我们一起来认识一下这个元件。
(1) 相关属性
DefaultPort该属性是用来指定作为客户端时要打开的端口号,也就是通过该端口来接收数据。
Active该属性是用来打开指定的端口号,设置为True表示打开端口,False表示关闭端口。
BroadcastEnabled该属性是用来设置是否用来实现广播,设置为True表示可以广播,False表示不能广播。
(2) 相关事件
OnUDPRead当客户端收到服务器端发来数据时触发该事件,通过该事件我们可以取得服务
器端发的每一帧的数据,以便在客户端回放。
除了以上提到的一些属性及事件外,TIdUDPServer还有一个重要的方法需要了解,那就是SendBuffer,通过该方法可以在服务器端向指定客户端的指定端口发送数据。
四、具体实现
1.服务器端程序
新建一个项目,并将包含到USES中。在窗体上加入一个控件TVideoCap,命名为VideoCap1,该控件用于视频捕获、显示;放置一个TIdUDPServer,命名为VideoSender,用于传输视频数据;放置两个文本框,CilentIP设置客户端IP,ClientPort设置发送到客户端的端口;一个复选框CheckSend用来决定是否向客户端传输视频;放置两个TButton控件,一个命名为“StartVideo”,用来打开视频,另一个命名为“Closevideo”,用来关闭视频,整体布局如图1所示。
图1 服务器端界面
定义发送数据包的结构:
type
VideoData=record
buf:array[0..8079] of byte; //压缩后的视频数据
Num:integer;//帧数据过大时,分几个数据包发送,数据包在这一帧中的编号
IsLast:boolean;//是否是这一帧的最后一个数据包
end;
定义全局变量:
Var
FCV: TCOMPVARS;//帧压缩结构
FInInfo: TBitmapInfo;//压缩时输入结构
FOutInfo: TBitmapInfo;//压缩时输出结构
FoutActSize: DWORD;//压缩后帧数据的大小
Buffer:^byte; //压缩后帧数据地址
Buf: array of Byte;// 压缩后帧数据
主要代码:
//填充BMP头结构
procedure tmapStruc;
begin
FillChar(der, SizeOf(TBitmapInfoHeader), 0);
with der do
begin
biBitCount := 24;
biCompression := BI_RGB;
biHeight := 240;
biPlanes := 1;
biSize := SizeOf(TBitmapInfoHeader);
biWidth := 320;
end;
end;
//初始化压缩引擎
procedure mpressor;
begin
FillChar(FCV, SizeOf(FCV), 0);
with FCV do
begin
dwFlags := ICMF_COMPVARS_VALID;
cbSize := SizeOf(FCV);
fccHandler := mmioFOURCC('d','i','v','x'); //选择压缩引擎,这里选择divx
fccType := ICTYPE_VIDEO;
hic := ICOpen(ICTYPE_VIDEO,fccHandler, ICMODE_COMPRESS);
lDataRate := 780;
lKey := 15;
lQ :=dword(ICQUALITY_DEFAULT);
if hic <> 0 then
begin
FillChar(FOutInfo, SizeOf(FOutInfo), 0);
ICCompressGetFormat(hic, @FInInfo, @FOutInfo);
ression:=BI_RGB;
ression:=fccHandler;
ICSeqCompressFrameStart(@FCV, @FInInfo);
end;
end;
end;
//FormOnShow事件
procedure ow(Sender: TObject);
begin
FillBitmapStruc;
InitCompressor;
//设置VideoCap1的相关属性
Index:=0;
ile:=false;
Open:=true;
review:=true;
end;
//StartVideoOnClick事件
procedure ideoClick(Sender: TObject);
begin
apture;
end;
// VideoCap1OnVideoStream事件
procedure ap1VideoStream(sender: TObject; lpVhdr: PVIDEOHDR);
var
KeyFrame:boolean;
MyVideo:VideoData;
i:integer;
begin
if d then
begin
FOutActSize:=0;
:=0;
//压缩帧数据
Buffer:= ICSeqCompressFrame(@FCV, 0, lpVHdr^.lpData, @KeyFrame, @FOutActSize);
SetLength(buf,FOutActSize);
Move(Buffer^, Buf[0], FOutActSize);
//当帧数据太大时,分几个数据包发送
while FOutActSize>8080 do
begin
:=+1;
:=false;
for i:=0 to 8079 do
begin
[i]:= buf[(-1)*8080+i];
end;
FOutActSize:=FOutActSize-8080;
// 向客户端发送数据包
ffer(,strtoint(),MyVideo,sizeof(MyVideo));
end;
if FOutActSize<8080 then
begin
:=+1;
:=true; //当前帧最后一个数据包
for i:=0 to FOutActSize do
begin
[i]:= buf[(-1)*8080+i];
end;
//向客户端发送数据包
ffer(,strtoint(),MyVideo,sizeof(MyVideo));
end;
end;
sMessages;
end;
//StopVideoOnClick事件
procedure TForm1. StopVideoClick (Sender: TObject);
begin
pture;
end;
//FormOnClose事件
procedure ose(Sender: TObject; var Action: TCloseAction);
begin
if <> 0 then
begin
ICSeqCompressFrameEnd(@FCV);
ICCompressorFree(@FCV);
ICClose();
end;
end;
2.客户端程序
新建一个项目,并将包含到USES中。在窗体上加入一个控件TImage,命名为Image1,该控件用于视频显示;放置一个TIdUDPServer,命名为VideoReceiver,用于接收视频数据,DefaultPot属性设置为6000,整体布局如图2所示。
图2 客户器端界面
定义全局变量:
Var
FCV: TCOMPVARS;//帧压缩结构
FInInfo: TBitmapInfo;//解压时输入结构
FOutInfo: TBitmapInfo;//解压时输出结构
FoutActSize: DWORD;//压缩后帧数据的大小
Buf: array of Byte;// 未解压时帧数据
myout:array[0..230399] of byte;// 解压后帧数据
主要代码:
//填充BMP头结构
procedure tmapStruc;
begin
//略,见服务器端程序
end;
//初始化解压缩引擎
procedure Compressor;
begin
//略,见服务器端程序
end;
//FormOnShow事件
procedure ow(Sender: TObject);
begin
FillBitmapStruc;
InitDeCompressor;
:=true;
end;
//VideoReceiverOnUDPRead事件
procedure eceiverUDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
RetVal:integer;
MyVideo:VideoData;
i:integer;
begin
RetVal:=-1;
on:=0;
//读取数据
ffer(MyVideo,sizeof(MyVideo));
if =1 then SetLength(buf,0);
SetLength(buf,()*8080);
for i:=0 to 8079 do
begin
buf[(-1)*8080+i]:=[i];
end;
if then //当前帧最后一个数据包
begin
//解压数据
RetVal :=
ICDeCompress(,0,@der,@Buf[0],@der,@myout);
if RetVal= ICERR_OK then
begin
//在Image1上画出一帧图像
StretchDIBits(,0,0,,,0,0,h,ht,@myout,FOutInfo,0,SRCCOPY );
t;
end;
end;
end;
//FormOnClose事件
procedure ose(Sender: TObject; var Action: TCloseAction);
begin
//略,同服务器端
end;
到此为止,整个网络视频程序就全部实现了。当然本程序的功能还有限,不过只要掌握了网络视频的的基本原理及相关知识后,根据需要在本程序的基础上进行扩充,完全可以成为一个实用的网络视频软件。本程序所有代码在Delphi7、Windows 2000/XP 及局域网中调试运行通过。
用Delphi开发视频聊天软件(一)
一、引言
我们知道视频聊天软件的关键技术在于采集视频,并实时传输给聊天软件在线的人。对于视频的采集,这里采用微软公司的关于数字视频的一个软件包VFW(Video for Windows)。相信很多人对它都很熟习,VFW能使应用程序通过数字化设备从传统的模拟视频源得到数字化的视频剪辑,VFW的一个关键思想是播放时不需要专用硬件。为了解决数字视频数据量大的问题,需要对数据进行压缩,而VFW引进了AVI的文件标准。该标准未规定如何对视频进行捕捉、压缩及播放,仅规定视频和音频该如何存储在硬盘上及在AVI文件中交替存储视频帧和与之相匹配的音频数据。通过VFW,开发人员通过发送消息或设置属性来捕捉、播放和编辑视频剪辑。当用户在安装VFW时,安装程序会自动地安装配置视频所需要的组件,如设备驱动程序、视频压缩程序等。VFW主要由6个模块组成。VFW功能模块:
包含执行视频捕捉的函数,它给AVI文件的I/O处理和视频、音频设备驱动程序提供一个高级接口
包含一套特殊的DrawDib函数,用来处理屏幕上的视频操作
包括对VFW的MCI命令解释器的驱动程序
包含由标准多媒体I/O(mmio)函数提供的更高的命令,用来访问.AVI文件
ICM 压缩管理器,用于管理的视频压缩/解压缩的编译码器(Codec)
ACM 音频压缩管理器,提供与ICM相似的服务,适用于波形音频
对于视频的传输,我们使用UDP来传,因为UDP传输速度快,TCP是面向连接的,建立连接时双方需经过三次握手,数据传输可靠,FTP、telnet等就是基于TCP的,UDP是面向非连接的,发出信息不需对方确认,但这样速度比TCP快,但有可能丢失数据,象SMTP、tftp等就是基于UDP的。另外UDP还支持广播,UDP广播两种,一种是directed broadcast,比如你的网段是192.168.0.X,你就往192.168.0.255发就可以了。另一种是limited broadcast,广播地址是255.255.255.255
二、视频聊天软件的开发步骤
1、创建捕捉窗口,采集视频
在进行视频捕捉之前必需要先创建一个捕捉窗口,并应以此为基础进行所有的捕捉及设置操作。捕捉窗口可用AVICap窗口类的"CapCreateCaptureWindow"函数来创建,其窗口风格可设置为WSCHILD和WS_VISIBLE参数。
有了捕捉窗口,我们就可以将视频流和音频流捕捉到一个AVI文件中;动态地同视频和音频输入器件连接或断开;用Overlay或Preview模式对输入的视频流进行实时显示,设置捕捉速率,显示控制视频源、视频格式及视频压缩的对话框,创建、保存或载入调色板,将图像和相关的调色板拷贝到剪贴板,将捕捉的单帧图像保存到BMP格式文件中。
2、捕捉窗口和驱动程序的关联
仅仅一个捕捉窗口是不能工作起来的,它必须要与一个设备相关联才能取得视频信号。用函数CapDriverConnect可使捕捉窗与其设备驱动程序相关联。
3、设置视频设备的属性
通过设置TcaptureParms结构变量的各个成员变量,可以控制设备的采样频率、中断采样按键、状态行为。设置好TcaptureParms结构变量后,可以用函CapCaptureSetSetup使设置生效。之后还可以用CapPreviewScale、CapPreviewRate设置预览的比例与速度,也可以直接使用设备的默认值。
4、打开预览
利用函数CapOverlay可选择是否采用叠加模式预览,以使系统资源占用小,视频显示速度加快。然后用CapPreview启动预览功能,这时就可以在屏幕上看到来自摄像头的图像了。
5、使用捕捉窗回调函数
前的四个步骤就可以建立一个基本的视频捕捉程序了,如果想自己处理从设备捕捉到的视频数据,则要使用捕捉窗回调函数来处理,比如一帧一帧地获得视频数据,也可以以流的方式获得视频数据等等。
6、传输视频流
使用回调函数可以取得第一帧的数据,我们使用网络技术将数据发给其它机器,其它机品将接收的数据显示出来。
7、接收视频
接收UDP数据,同时将接收到的数据回显出来,这样就可以看到远处传来的视频了。
三、用Delphi编写程序代码
微软的VFW SDK只有VC和VB版,并没有Delphi版,不过在网上可以找到文件,文件声明了调用DLL中的各个函数和变量。(注:源代码中提供了文件)
下面就以Delphi7开发一个网络视频聊天软件,聊天软件分两个程序,一个是视频采集程序并进行UDP广播的视频聊天软件服务器,另一个是接收UDP广播程序显示传来的视频数据的视频聊天软件客户端。
1、建立视频聊天软件服务器
1)新建一个工程,命名为,并把加到USE中
2)在Form1上放置一个Tpanel控件,该控件用于显示视频。之后再放置两个Tbutton控件,一个caption为"开始",另一个Name为"停止",放置一个UDP组件,这里用indy的IdUDPClient用来传输视频,如图示:
3)定义全局变量
CapWnd:THandle; //定义捕捉窗句柄
CapParms:TcaptureParms; //用于设置设备属性的结构变量
BMPINFO:TBitmapInfo; //BMP图像信息
4)编码事件代码
开始按钮代码:
CapWnd := capCreateCaptureWindow('我的窗口', WS_VISIBLE or WS_CHILD,//窗口样式 0, //X坐标 0, //Y坐标 , //窗口宽, //窗口高 , //窗口句柄 0); //通常为0
if CapWnd = 0 then exit; //定义帧捕捉回调函数
CapSetCallbackOnFrame(CapWnd,FrameCallBack);
estMicroSecPerFrame:=1;
Enabled:=FALSE;
reAudio:=FALSE;
ntrol:=FALSE;
:=TRUE;
ort:=VK_ESCAPE;
LeftMouse:=False;
RightMouse:=FALSE; //让设置生效
CapCaptureSetSetup(capWnd,@CapParms,sizeof(TCAPTUREPARMS));
CapPreviewRate(capWnd,33); //设置预览视频的频率
CapCaptureSequenceNoFile(capWnd); //如果要捕捉视频流,则要使用函数来指定不生成文件,不然会自动生成AVI文件 CapDriverConnect(CapWnd,0); //连接摄像头设备,第二个参数是个序号,当系统中装有多个显示驱动程序时,其值分别依次为0到总个数如果有多个摄像头,那么就是0->1->2
capGetVideoFormat(capWnd, @BMPINFO,sizeof(TBitmapInfo)); //取得视频图像数据头
CapPreviewScale(capWnd,TRUE); //是否缩放
CapOverlay(capWnd,true); //指定是否使用叠加模式,true为使用,否则为falseCapPreview(capWnd,true);
回调函数代码:
var hd:Thandle;
jpg:TJpegImage;
memStream :TMemoryStream;
Bitmap:TBitmap;
begin //将数据显在Image,
Bitmap:=;
:=h;
// New size of Bitmap
:=ht;
hd:= DrawDibOpen; DrawDibDraw(hd,,0,0,_
h,ht,_
@der,lpVHdr^.lpData,0,0,h,_
ht,0);
DrawDibClose(hd); //发送数据
memStream := ;
jpg := ;
(Bitmap);
ssionQuality := 10; //jpg压缩质量
eded;
ss;
Stream(memStream);
; //因为UDP数据包有大小限制,这里如果超出部分,就没有传输,完全可以发几次发出去
astEnabled:=true;//用广播功能
if >Size then
//向192.168.0.X网段广播,端口 9001
ffer('192.168.0.255',9001,^,Size) else
ffer('192.168.0.255',9001,^,);
;
;
End;


发布评论