IdUdpClient抓屏使用Memorystream出错请教(Jpeg Error #53)(50分)

  • 主题发起人 主题发起人 sxdthonda
  • 开始时间 开始时间
S

sxdthonda

Unregistered / Unconfirmed
GUEST, unregistred user!
大家好,我现在使用Indy9的IdUdpClient和IdUdpServer控件。思路是先设置IdUdpClient和IdUdpServer的IP及端口,然后调置Active为True,当用户点Button1时,抓屏,然后用Idudpclient发送,Idudpserver获取数据。

程序代码段:
procedure TForm1.FormCreate(Sender: TObject);
begin
picstream:=tmemorystream.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
picstream.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
jpic:tjpegimage;
bpic:tbitmap;
s:string;
begin
jpic:=tjpegimage.Create;
bpic:=tbitmap.Create;
bpic.Width :=100;//screen.width;
bpic.Height :=100;//screen.Height ;
bitblt(bpic.Canvas.Handle ,0,0,bpic.Width ,bpic.Height ,getdc(0),0,0,srccopy);
jpic.Assign(bpic);
jpic.CompressionQuality :=10;
jpic.Compress;
jpic.SaveToStream(picstream);
// compressStream( picstream);
picstream.Position :=0;
//jpic.SaveToFile('d:1.jpg');
bpic.Free;
jpic.Free;
showmessage(inttostr(picstream.Size));
form1.IdUDPClient1.sendbuffer(picstream,picstream.size);
picstream.SaveToFile('d:/picstream.jpg');
picstream.Clear;
end;

procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
jpic:tjpegimage;
getstream:tmemorystream;
begin
adata.Seek(0,0);
getstream:=tmemorystream.Create;
getstream.Position:=0;
getstream.LoadFromStream(adata);
getstream.SaveToFile('d:/dest.jpg');
jpic:=tjpegimage.Create;
getstream.Position :=0;
jpic.LoadFromStream(getstream); --运行这句时就出错。
image1.Picture.Bitmap.Assign(jpic);
jpic.Free;
getstream.Free;
end;

而且检查生成的二个Jpg文件,发现'd:/picstream.jpg'可打开,而'd:/dest.jpg'无法打开,但二个文件的大小一样。请大家指点一下。多谢了。
 
大哥,不知道你解决这个问题没有,我现在也遇到了相同的问题,我抓了几次,发现一次发送的包最大不过8192,而你抓的图片可能超出了范围,所以你的而'd:/dest.jpg'才会打不开.而那个jpeg的错误就是这样产生的.
不知道大哥解决没有.如果解决了,麻烦告诉我解决的方法好吗?
 
通过控制
bpic.Width :=100;//screen.width;
bpic.Height :=100;//screen.Height ;
这两行就可以控制抓图的大小,我试过的,小了也不行了。

听别人说是传送TMemoryStream的问题。但也没有一个明确人答案。
 
我现在能够将抓的图片显示出来,就是显示有些延时,用tmemorystream也是有问题的,它总是有时候可以,有时候内存分配不够,很头疼
我的qq是:492354682.讨论一下怎么去改进它
 
[red]adata.Position:=0;[/red]
getstream.LoadFromStream(adata);
 
我用的是clientsocket传输,用buffer去接受,再读到memorystream里去,就是在读到内存流去的时候报错,不管memorystream是全局的还是局部的都是这样
 
adata.Position:=0;
getstream.LoadFromStream(adata);

加了这句也不行,我看了一下发送了接收的文件。字节大小一样,内容完全不同。可真不知问题出在哪里?
 
问题在于你发送的文件可能太大了,一般我发的是250个字节,但有延时,而且,哪个memorystream时不时的给你出点问题,头痛死了
 
可以将tidudpclient的buffsize改大些 最大可为65536 即使是这样一般全屏的图片一次还是发不完 要将图片压缩后 然后分成几个包发过去
 
你发送的文件可能太大了
 
http://www.delphibbs.com/keylife/images/u196832/UDP传输语音视频.rar
有用流发送视频并压缩

或以下是我的程序中一部发和接收
const MAX_BUFFERCOUNT = 4096 - 1;
function Tfm_Client.SendScreen(const S: TSocket; Left, Top, Width, Height: Integer): Boolean;
var
DC: HDC;
TopHwnd: HWND;
FJpeg: TJpegImage;
FBitmap: TBitmap;
FStream: TMemoryStream;
Buf: array[0..MAX_BUFFERCOUNT] of Char;
SendLen: Integer;
intOffset: Int64;
begin
Result := False;
TopHwnd := GetDesktopWindow;
DC := GetDC(TopHwnd);
FStream := TMemoryStream.Create;
FBitmap := TBitmap.Create;
FJpeg := TJpegImage.Create;
FBitmap.Height := Height;
FBitmap.Width := Width;
try
Windows.BitBlt(FBitmap.Canvas.Handle, 0, 0, Width, Height, DC, Left, Top, Windows.SRCCOPY);
FJpeg.Assign(FBitmap);
FJpeg.JPEGNeeded;
FJpeg.CompressionQuality := 50;
FJpeg.Compress;
FJpeg.SaveToStream(FStream);
FStream.Position := 0;
if FStream.Size < 0 then Exit;
Sleep(100);
//发送取屏指令
SendLen := SK_SysDesktop;
SendLen := ClientSendCommand(SendLen, nil, FStream.Size);
if SendLen < 0 then Exit;
intOffset := 0;
while True do
begin
FStream.Position := intOffset;
SendLen := FStream.Read(Buf, SizeOf(Buf));
//发送屏幕数据
SendLen := SendBuf(S, Buf, SendLen);
//错误检测
if SendLen = SOCKET_ERROR then
if CheckErrorBySotp(CSocket.Client, WinSock.WSAGetLastError) then
begin
fm_Client.Memo1.Lines.Add('Send Stream for Jpeg error: WSAEWOULDBLOCK');
fm_Client.OnClientClose(CSocket.Client);
Exit; //Break;
end;
intOffset := intOffset + SendLen;
if IntOffset >= FStream.Size then
Break;
end;
fm_Client.Memo1.Lines.Add(Format('图形大小: %d, 发送大小: %d, 错误发送: %d',
[FStream.Size, intOffset, FStream.Size - intOffset]));
fm_Client.Image1.Picture.Bitmap.Assign(FJpeg);

finally
Windows.ReleaseDC(TopHwnd, DC);
if FBitmap <> nil then FBitmap.Free;
if FJpeg <> nil then
FJpeg.Free;
if FStream <> nil then
FStream.Free;
end;
end;

procedure Tfm_server.ReadClientDesktop(const Socket: TCSocket);
var
Buf: array [0..MAX_BUFFERCOUNT] of Char;
ReceLen: Integer;
FJpeg: TJpegImage;
Stream: TMemoryStream;
StreamSize: Int64;
begin
Sleep(200);
Stream := TMemoryStream.Create;
FJpeg := TJpegImage.Create;
try
ReceLen := ReceiveBuf(Socket.Client, StreamSize, SizeOf(StreamSize));
if (ReceLen <> SizeOf(StreamSize)) or (StreamSize <= 0) then Exit;
repeat
FillChar(Buf, SizeOf(Buf), #0);
ReceLen := ReceiveBuf(Socket.Client, Buf, SizeOf(Buf));
if ReceLen > 0 then
Stream.Write(Buf, ReceLen)
until ReceLen <= 0;
if Stream.Size = StreamSize then
try
Stream.Position := 0;
FJpeg.LoadFromStream(Stream);
Image1.Picture.Bitmap.Assign(FJpeg);
except
on E: Exception do
Memo_Socket_log.Lines.Add(E.Message);
end;
finally
Stream.Free;
FJpeg.Free;
end;
end;
 
有人建议发送Memorystream.memory,说直接发送Stream是发出一个指针,内容会不同,但也没有成功。看来一般都是使用一个字串数组,分长度发送。


谢谢jfyes大侠的答复,我先去测试一下。
 
你试下倒过来发,用serversocket发给clientsocket,这样好象可以,我试过的
 
getstream.Seek(0,soFromBeginning);
jpic.LoadFromStream(getstream); --运行这句时就出错。
 
不管用那端收发都行。你必须确定你的数据真正发送出去。
网上这样的例子太多了,你找几个仔细细看看就明白了。
 
[:(],测试没有通过,但我有一个新的发现,不知大家知道不。使用Memorystream通过Udp发送时,不能直接发送流,需要把流转化一下发送。现在程序发送较小图片时能正常显示了,大图片,分片发送问题还没处理。程序贴上来,请大家再帮帮忙。

procedure Tfrmclient.Button1Click(Sender: TObject);
var
DC:hdc;
Fjpeg:tjpegimage;
fullscreencanvas:tcanvas;
Fbitmap:tbitmap;
buf:array [0..4096] of char;
sendlen:integer;
intoffset:int64;
sourceRect,destRect:tRect;
begin
idudpserver2.DefaultPort:=9090;
idudpserver2.Binding.IP:='127.0.0.1';
idudpserver2.Active:=true;
try
dc:=getdc(0);
fullscreencanvas:=tcanvas.Create;
fullscreencanvas.Handle:=dc;
fbitmap:=tbitmap.Create;
fbitmap.Width:=100; //screen.Width;抓全屏图片就大,运行就出错了。
fbitmap.Height:=100; //screen.Height;
sourceRect:=rect(0,0,screen.Width,screen.Height);
destRect:=rect(0,0,screen.Width,screen.Height);
fbitmap.Canvas.CopyRect(sourcerect,fullscreencanvas,destrect);
fjpeg:=tjpegimage.Create;
fjpeg.Assign(fbitmap);
fjpeg.CompressionQuality:=10;
fjpeg.SaveToStream(fstream);
fstream.Position:=0;
image1.Picture.Bitmap.Assign(fjpeg);
fjpeg.SaveToFile('d:/20060404-1.jpg');
if fstream.Size<0 then exit;
intoffset:=0;
while true do
begin
fStream.Position:=intoffset;
sendlen:=fstream.Read(buf,sizeof(buf)); //把流转换一下,从jfyes大侠程序学到的。
idudpclient1.SendBuffer(buf,sendlen);
intoffset:=intoffset+sendlen;
sleep(100);
if intoffset>=fstream.Size then break;
end;
finally
releasedc(0,dc);
fbitmap.Free;
fjpeg.Free;
fstream.Clear;
end;
end;

procedure Tfrmclient.IdUDPServer2UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
const filename='d:/20060404.jpg';
begin
adata.Seek(0,0);
if fileexists(filename) then deletefile(filename);
if mem<>nil then
begin
mem.Free;
mem:=nil;
end;
if not fileexists(filename) then
mem:=tfilestream.Create(filename,fmopenreadwrite or fmcreate)
else
mem:=tfilestream.Create(filename,fmopenreadwrite );
if mem<>nil then
begin
mem.CopyFrom(adata,adata.Size);
end;
stream.Clear;
adata.Seek(0,0);
stream.LoadFromStream(adata);
stream.Position:=0;
ffjpeg.LoadFromStream(stream);
image2.Picture.Bitmap.Assign(ffjpeg);
end;
如果图片较小时,看保存下的图片正确,而且屏上显示的图片也很正常。可如果抓下的图大了,保存的文件就不正常了,而且出现JPEG #53错误。
 
我考虑可能是在设计这些控件的时候就把他们设成了非对称的模式,你可以试下用server端来发送,client端来接受,我就是这样做的,而且发送tmemorystream也不会出错。
(不知道我理解的对不对,还请高手们指教)
 
多谢大家的帮助,我最后使用一个折中的方法处理问题了。详细代码就给大家贴出来了,可供参考一下。主要思想是,客户端放置一个Timer构件,先发送一个‘Send’,如果服务端有响应‘OK’,就开始发送数据,否则就等下一个时段检查。
服务端最后使用了一个TFilestream,将接收的数据保存为一个文件了。(对接收TMemorystream还是不会[:(])

客户端程序:
unit clientUnit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdAntiFreezeBase, IdAntiFreeze, IdBaseComponent, IdComponent,
IdUDPBase, IdUDPClient, ExtCtrls,jpeg;

type
Tfrmclient = class(TForm)
Timer1: TTimer;
IdUDPClient1: TIdUDPClient;
IdAntiFreeze1: TIdAntiFreeze;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
frmclient: Tfrmclient;
Fstream : TMemorystream;

implementation

const bufsize=4096-1;

{$R *.dfm}

procedure Tfrmclient.FormCreate(Sender: TObject);
begin
Fstream:=TMemorystream.Create;
Timer1.Interval:=30000;
idudpclient1.Host:='172.16.88.3';
idudpclient1.Port:=9090;
// idudpclient1.Active:=true;
end;

procedure Tfrmclient.FormDestroy(Sender: TObject);
begin
Fstream.Free;
end;

procedure Tfrmclient.Timer1Timer(Sender: TObject);
var
receivedstring,filename:string;
dc:hdc;
fjpeg:tjpegimage;
fscreencanvas:tcanvas;
fbitmap:Tbitmap;
buf:array[0..bufsize] of char;
sendlen:integer;
intoffset:int64;
sRect,Drect:TRect;
begin
try
filename:=formatdatetime('yyyymmddhhnnsszzz',now);
idudpclient1.Send('send'+filename);
receivedstring:=idudpclient1.ReceiveString();
if uppercase(receivedstring)='OK' then
begin
dc:=getdc(0);
fScreencanvas:=Tcanvas.Create;
Fscreencanvas.Handle:=dc;
fbitmap:=tbitmap.Create;
fbitmap.Width:=screen.Width;
fbitmap.Height:=screen.Height;
sRect:=Rect(0,0,screen.Width,screen.Height);
dRect:=Rect(0,0,screen.Width,screen.Height);
fbitmap.Canvas.CopyRect(sRect,fscreencanvas,dRect);
fjpeg:=Tjpegimage.Create;
fjpeg.Assign(fbitmap);
fjpeg.CompressionQuality:=8;
fjpeg.SaveToStream(fstream);
fstream.Position:=0;
// if fstream.Size<0 then exit;
intoffset:=0;
while true do
begin
if intoffset>=fstream.Size then break;
fstream.Position:=intoffset;
sendlen:=fstream.Read(buf,sizeof(buf));
idudpclient1.SendBuffer(buf,sendlen);
application.ProcessMessages;
intoffset:=intoffset+sendlen;
end;
end;
releasedc(0,dc);
fbitmap.Free;
fjpeg.Free;
fstream.Clear;
idudpclient1.Send('end');
except
//idudpclient1.Send('end');
end;
end;

end.

服务端程序:
unit serverUnit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdAntiFreezeBase, IdAntiFreeze, IdBaseComponent, IdComponent,
IdUDPBase, IdUDPServer, StdCtrls,IdSocketHandle;

type
Tfrmserver = class(TForm)
Button1: TButton;
IdUDPServer1: TIdUDPServer;
IdAntiFreeze1: TIdAntiFreeze;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
private
{ Private declarations }
public
{ Public declarations }
end;

var
frmserver: Tfrmserver;
mem : Tfilestream;

implementation

{$R *.dfm}

procedure Tfrmserver.Button1Click(Sender: TObject);
begin
idudpserver1.DefaultPort:=9090;
idudpserver1.Active:=true;
end;

procedure Tfrmserver.Button2Click(Sender: TObject);
begin
idudpserver1.Active:=false;
end;

procedure Tfrmserver.FormDestroy(Sender: TObject);
begin
if mem<>nil then mem.Free;
mem:=nil;
end;

procedure Tfrmserver.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
str,filename:string;
begin
adata.Seek(0,0);
setlength(str,adata.size);
adata.Read(str[1],adata.Size);
if uppercase(str)='END' then
if mem<>nil then begin mem.free;mem:=nil;exit; end;
if pos('SEND',uppercase(str))>0 then
begin
filename:=str;
delete(filename,1,length('SEND'));
filename:='d:/temp/'+abinding.PeerIP+'-'+filename+'.jpg';
if fileexists(filename) then deletefile(filename);
str:='OK';
Abinding.SendTo(abinding.PeerIP,abinding.PeerPort,str[1],length(str));
if mem<>nil then
begin
mem.free;
mem:=nil;
end;
if not fileexists(filename) then
mem:=TFilestream.Create(filename,fmopenreadwrite or fmcreate)
else
mem:=TFilestream.Create(filename,fmopenreadwrite);
end
else
if uppercase(str)<>'END' THEN
begin
if mem<>nil then
begin
adata.Seek(0,0);
mem.CopyFrom(adata,adata.Size);
end;
end;
end;

end.
 
多谢大家的帮助了。程序还有许多问题有待解决。欢迎大家有时间再讨论交流。
一、多线程如何处理(有多个客户端时);
二、接收TMemorystrem,直接显示在服务端屏上如何处理。

这二个问题我还不会。
 
我有种预感,你的程序这样做将会出现更大麻烦!
 
后退
顶部