You are on page 1of 3

摘要本文介绍了两种方法以实现图像的快速传输,并给出关键代码,稍加修改可应用于网络教室中。

关键字快速传输,逐行扫描,分块压缩 -

Bmpl:TB.tmap:
一、刖昌 m 12:PByteArray:
begjn
大图像的传输一直是热门话题,尤其是屏幕图像,如何快 Jf intfIag=1 then

速而又不很失真地传输,一直捆扰着众多编程人员。目前最基 Begin
mem01.丁ext:=~:
本的方法是使用流来传,即:抓屏一压缩(转成JPEG或直接
Strdata:=socket.ReceiveText: //接收行号
压缩)一发送一接收一显示。但这种方法的缺陷很明显,速度 mem01.1ines.add(Strdata):
很慢,更不可能用于网络教室中,究其原因,是因为每次发送 sOcket.SendTe×t《’run’): //行号接收完,通知接收数据
的数据量过大(800%600 24bit需要800}600}24/8/1024卡 IntFIag:=2: //转入socket接收数据的状态
end:
3/1024=4.11M)。本程序使用了两种不同的方法,一种是逐
lf IntFIaq=2 then
行扫描,一种是将图像分块压缩,化大数据为小数据,再发 Begin

送。 MySize:=S0cket.ReceiveLength: //接收包长度
GetMem(12,imagel.Width);
方法一:
Bmp 1:=TB.tmap Create:
实质上讲,SOCKET每次发送的并不是图像,而是数据量 Bmpl.Assign(Imagel.Picture.Bitmap):
要小得多的颜色值,这样就实现了快速传送。同时,在每次传 bmp1.PixeI FOrmat:=pf8bit:

输行数据之前,将该行数据与上次抓到的屏幕图像的相同行数 if mysize=imagel.width then//如果整行数据接收完毕


then
据进行比较,如果不同,则发出去,如果相同则跳过此行。这
for i:=O to Mem01.Lines.Count一1 do
就避免了发送冗余数据。这样一来,s0CKET每次发送的数据 begin

量就很小了。 sleep(O): //延时


Socket.ReceiveBuf{12“,MvSize):
程序原理至此已介绍完毕,但如果仅写出上面原理的实
s1:=mem01.L{nes.St ringsfj】:
现,会发现无法正确地接收到图像,这是因为SOCKET传输的 |1:=Bmpl.ScanLine【strtoint(s1)】://扫描原行,并得
是数据流,而不是数据包这个问题没有考虑到,这就导致了每 到这行的数据
Move《124,旷,imagel.Width)://将接收到的数据
一次接收到的不一定就是一行的数据,重则扫描越界而导致崩
覆盖原数据
溃,轻则图像错位无法复原。本文的两种方法均采用了延时函 end:

数来协调同步。 end:

下面给出该方法的详细代码: Picture.Bitmap.Assign(Bmpl)://所有行数据转换
lmagel

完成!显示转换后的图象
注:实际应用中应该比本程序多一个发送初始屏幕图像的
Bmpl.Free:
步骤,本程序因作为演示用,用抓取本机屏幕图像来替代。 Freemem(12):
end:
客户端:
TForm 1.8utton2Cfick(Sender:TObfect)j 这是单独一帧图的接收过程,实际应用中需连续接受,必
procedure

begin 须在另一个过程中调用一个ⅡMER。
csk.Socket.SendTe×t(’cap 7): //发送数据标志为7cap procedure TForml.TimerlTimer(Sender:TObject):
IntFIag:=1: //标志SOCKET的未接收状态 beg.n
end:
button2CIick(Sender):
procedure TForm 1.CskRead(Sender: TO bject: Socket:
imagel.Refresh:
TCustomWinSocket): end:
Var Strdata,s1,s2:Stmg: 详见盘内代码。
MySize,j,x,n:Integer:

万方数据
服务端:
方法 速度(/幅) 颜色 运行情况 调试环境
procedure TForm 1.SckCiientRead(Sender: TObiect: Socket:
最快221ms JPEG压缩 鼠标有明显 P41.3G.128MB.
TCustomWinSocket):
流方法 正常791ms 比40% 停滞感。影 TⅣr2 M64 32MB
va r StrData,s1:String:
最慢971ms 响正常操作 800}600 16/32bit
11,12:PByteArray:

n,x,ActiVers,j,k:fnteger: 最慢379m8 16bit真彩色 在时间间隔很 1024宰768 16/32bit


bml,bm2:TBitMap:
本程序 普通180ms 短时有偶尔停 Win2K Pr0妇fesional
src:hdc:
最快10ms 滞感,不影响 TP—LINK 10M
begin
StrData:=S0cket.RecefveText: 正常操作

then //收到发送标志,准备工作
”Strdata=’cap 说明:因为本程序在Win2000下编写通过,程序在win98
Begin
下运行会发生无法预料的错误,请尽量在win2000下调试该
timerl Enabled:=true: //连续截屏
mem01。Te×t:=~: 程序。
for i:=O t0 bmpl.Height一1 do 鉴此,在这我提供给大家一种类似于本文要介绍的第2
Begin
种方法的写法(已在98下调试通过):
11:=bmpl.scanune…://第N一1次图像第l行数据
Server:
12:=bmp2.ScanL;ne…: //第N次图像第1行数据
Var buf:TmemOryStream:
if not CompareMem(11,f2,imagel.Width)then
buffarrayIO..1027】of byte://屏幕宽度+Sizeof《integer)长
//比较2者
Procedure丁Form 1.Button2CIick(Sender:TObiect):
//如果不同
begin
BegJn
buf:=TMemoryStream.Create; //建立内存流
mem01 Lines Add(;nttostr《i)): //记录]:i行
fOr i:=0 to 766 dO
End:
begin
end:
11:=bmpl ScanLine【i】:
Socket.SendText《Trim《mem01 Te×t)): //将所有不同 buf writeBuffer《11“,lmagel.wjdth》://将第l行数据写
行行号发出 入流
end:
buf Seek{0,soFromEnd)://定位到流尾部
f strdata=7 run then //接收到发送数据标志 buf writeBuffer(i,sizeof《integer))://写入该行行号
Begin
buf.PosjtIon:=O; //复位
for j:=0 to Mem01.Lines.Count一1 do //比较已记 buf.ReadBuffer《buff,1028)://将流里的数据读入数组中
录下行号的数据 cs.Socket.Sendbuf(buff,1 028):
begin sleep(O)://延时
s1:=mem01.Ljnes StringsIj】: buf,Clear://清空流,否则在下一个循环中就有冗余数据
11:=Bmpl.ScanLine【Strtoint(s1)]: end:
12:=bmp2 ScanLine【Strtoint(s1)】: C|ient:
if not comparemem(11,12,;magel.Width)then Var buf:arrav【O..1027】of bvte:
begin MVstream:TmemorvStream:
Socket.SendBuf(f2“,imagel.W.dth): //将数据发出 Procedure TForm 1.ssCfientRead(Sender: TObiect: Socket:

sleep(0)://同样要延时接收! TCustomWinSocket):
end: Var

end: h 12:pBytearray:

bmpl.Assfgn(bmp2》://将第N一1次图像转换为第N次 i,j:integer:
图像,用来和第N+1次图像比较 begin

end: i:=socket.ReceiveLength://接收包长度
end: MyStream:=TMemoryStream.Create:

procedure TForm 1 Timerl Timer f Sender:TObject): Getmem…,1024》://分配C Jjent屏幕宽度大小的内存


ifi=1028 then//如果一行{宽度+sizeof(integer))接收完整
begin

CapSrc://此为程序中一自定义函数,用来截屏并转换图像 begin
socket.ReceIvebuf(buf,sizeof(buf)):
的真彩色
end: Mystream.WrjteBuffer(b uf,i)://将数组写入流
Mystream.Seek(一sizeof(integer),soFromEnd): //定位
以下为该方法与流实现的运行情况比较:
到倒数第一个整形
注:以下速度结果在127.0.0.1上使用GetTickCoum()测 Mystream ReadBuffer《j,sizeof(meger))://读出该行行号
得。 Mystream Seek《O,soFromBeginning)://定位到首部

万方数据
Mystream.ReadBu什e rIll“,1024)://读出行数据 bmpl.SaveToStreamIbuf)://将图象写入流中
J2:=bmpl.ScanLjne…: buf.POsjtiOn:=0:

CopyMemory{12,h 1024)://覆盖 Seek(0,soFromEnd)://定位到流尾部


buf

ifj=766 then//如果所有行转换完毕 writeBu什er(.nt×,sizeof(integer))://向流尾部写入该


buf

begin 图象的编号
imagel Picture.Bitmap.Assign l bmpl): //显示 bufPosition:=0://复位,否则Sendstream方法无法发送该流
end: //在这用压缩算法将待发送的流压缩
end: //c|ientsocketl.socket.sendstream(buf):
freemem《11): end:

end: procedure Tmythread.Execute:


这种方法在Win2000和Win98下均调试通过,不过速度 begin
FreeonTemlinate: =true:
上略慢于方法一,主要是时间用在流的读取和写入上。
EnterC rit;caISection《CS):/列缶界区
方法二:由于篇幅限制,在这只给出思路和SeⅣer的简化 case intx Of

代码: 1:洲:=31://第1块
2:洲:=62://第2块
程序中使用了一副186+11l的图片作测试,将之竖着分
3:洲:=93://第3块
为6块,每块31・111大小(因为这只作为演示用,所以没有 4:洲:=124://第4块
分块算法,大家在应用时可以自己写分块代码;如果是用于屏 5:吖:=155://第5块
6:洲:=1 86://第6块
幕图像传输中则需先将后一幅屏幕截图与上一幅图进行异或操 end:

作,这样相同部分就为黑色,用LHA或其他压缩算法将之压 with Mvrect dO

缩,数据量将会变得很小,这样传输起来也是比较快的。) begin

tOp:=0:
Server:
Ieft:=d.f一31:
type
right:=dif:
TMvthread=cIass(Tthread)
bottOm:=forml.Imagel.Height:
D rjvate
end:
int×:integer:
synchronize《ChangeCap)://线程同步
Drotected
LeaveCriticalSection{CS)://结束临界区
procedure E×ecute:Override:
end:
prOcedure ChangeCap:
procedure TForm 1.B;t8tn 1 C Jick(Sender:TObject):
PubIic
var i:integer:
constructor Create《ns:integer):
begin
end:
bmpl:=Tbitmap.Create:
Var
TrdLst:=丁LIst,Create:
bmpl:TBitmap:
㈨tla|izeC㈨calsection(CS)://装载临界区
FOrml:TFOrml:
for i:=1 to 6 do TrdLst.Add(丁Mvthread.Create(i}): //开
MVRect:Trect:
启6个线程并加入歹0表中
Trdfst:TLlST:
end:
CS:TRTLCritfcafSection:
buf:TmemorVStream:
Dif:integer:
constructor TMythread.Create(ns:integer):
根据上面代码,186十111的图片分成6块传送,client端
begin
intX:=ns: 程序大家可以自己写,这里就不在给出代码。
inherited create(faIse): 在C1ient端,接受到发来的流后,先用相应的解压缩算法
end:
还原流,再把流尾部的编号读出,根据事先定制好的Rect把
procedure 丁Mythread。ChangeCap;
var j:integer: 流再kad到Bitm印画到imge上。
begin
至次两种方法已介绍完毕,方法一中的截屏速度是可调
bmpl.Width:=myRect.Right:
的,调整Timrl.Intenral即可,程序中socket发送的关键在于
bmpl.Height:=myRect BOttom:
bmpl.Canvas.CopyRect(Myrect, 用Sleep来延时,所以如果在无法传送时可试着改变Sleep(x)
forml.imagel.Picture.Bitmap.CanVas,Myrect);//Copy分 的X。
图象
buf:=TMemorvstream.Create: (收稿日期:2003年4月20日)

万方数据

You might also like