问题
I'm trying implement a example of Client > Server where the Client must be able to send frames of a live webcam. In my tests the Client is sending fine, but on Server side i'm not receiving any frame, only the total of bytes sent and the lines after of:
if Stream.Size = stSize then
never are executed.
Someone know why this is happening? probably because is a big flux of data being received?
This is my code:
Server:
unit Server;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.ExtCtrls, Vcl.StdCtrls, System.Win.ScktComp, Vcl.Imaging.jpeg;
type
TCam_Thread = class(TThread)
private
Socket: TCustomWinSocket;
protected
procedure Execute; override;
public
constructor Create(aSocket: TCustomWinSocket);
end;
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
ServerSocket1: TServerSocket;
procedure ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure Button1Click(Sender: TObject);
procedure ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TCam_Thread.Create(aSocket: TCustomWinSocket);
begin
inherited Create(true);
Socket := aSocket;
FreeOnTerminate := true;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ServerSocket1.Port := 1234;
ServerSocket1.Active := true;
end;
procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket);
var
WC: TCam_Thread;
begin
WC := TCam_Thread.Create(Socket);
WC.Resume;
end;
procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
//ShowMessage('Client connected!');
end;
procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
//ShowMessage('Client disconnected!');
end;
procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
ErrorCode := 0;
Exit;
end;
procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket);
begin
ShowMessage('Server listen on port: ' + IntToStr(Socket.LocalPort));
end;
procedure TCam_Thread.Execute;
var
s: string;
Stream: TMemoryStream;
Receiving: Boolean;
stSize: Integer;
jpg: TJpegImage;
begin
while not Terminated and Socket.Connected do
begin
if Socket.ReceiveLength > 0 then
begin
s := Socket.ReceiveText;
if not Receiving then
begin
if Pos(#0, s) > 0 then
begin
stSize := strToInt(Copy(s, 1, Pos(#0, s) - 1));
Synchronize(nil,
procedure
begin
Form1.Caption := 'Size of data received: ' + intToStr(stSize div 1024) + ' KBs';
end);
end;
Stream := TMemoryStream.Create;
Receiving := true;
Delete(s, 1, Pos(#0, s));
end;
try
Stream.Write(AnsiString(s)[1], length(s));
if Stream.Size = stSize then
begin
Stream.Position := 0;
Receiving := false;
jpg := TJpegImage.Create;
jpg.LoadFromStream(Stream);
Synchronize(nil,
procedure
begin
Form1.Image1.Picture.Assign(jpg);
end);
Stream.Free;
jpg.Free;
end;
except
Stream.Free;
jpg.Free;
end;
end;
end;
end;
end.
Client:
unit Client;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.ExtCtrls, System.Win.ScktComp;
type
TForm1 = class(TForm)
Panel1: TPanel;
ClientSocket1: TClientSocket;
procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
uWebCam;
{$R *.dfm}
procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
begin
Camera := TCamera.Create(Panel1, Socket);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
ClientSocket1.Host := '192.168.0.10';
ClientSocket1.Port := 1234;
ClientSocket1.Active := True;
end;
end.
uWebCam.pas
unit uWebCam;
interface
uses
Windows, Messages, Classes, SysUtils, Graphics, Controls, ExtCtrls, Jpeg,
ScktComp;
type
PVIDEOHDR = ^TVIDEOHDR;
TVIDEOHDR = packed record
lpData: PBYTE;
dwBufferLength: DWORD;
dwBytesUsed: DWORD;
dwTimeCaptured: DWORD;
dwUser: DWORD;
dwFlags: DWORD;
dwReserved: array[0..3] of DWORD;
end;
type
TCamera = class(TObject)
private
Parent: TPanel;
VideoHwnd: HWND;
Sock: TCustomWinSocket;
procedure Resize(Sender: TObject);
public
constructor Create(Owner: TPanel; Socket: TCustomWinSocket);
destructor Destroy; override;
procedure SetSize;
procedure SetSource;
end;
var
Camera: TCamera;
implementation
uses
uDrawDib;
const
WM_CAP_START = WM_USER;
WM_CAP_STOP = WM_CAP_START + 68;
WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10;
WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11;
WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START + 41;
WM_CAP_DLG_VIDEOSOURCE = WM_CAP_START + 42;
WM_CAP_SET_PREVIEW = WM_CAP_START + 50;
WM_CAP_SET_PREVIEWRATE = WM_CAP_START + 52;
WM_CAP_SET_SCALE = WM_CAP_START + 53;
WM_CAP_GRAB_FRAME = WM_CAP_START + 60;
WM_CAP_SEQUENCE = WM_CAP_START + 62;
WM_CAP_SET_CALLBACK_FRAME = WM_CAP_START + 5;
WM_CAP_GET_VIDEOFORMAT = (WM_CAP_START + 44);
function capCreateCaptureWindowA(lpszWindowName: PChar; dwStyle: LongInt; x: Integer; y: Integer; nWidth: Integer; nHeight: Integer; ParentWin: HWND; nId: Integer): HWND; stdcall; external 'AVICAP32.DLL';
function capFrameCallback(hCapWnd: HWND; lpVHDR: PVIDEOHDR): DWORD; stdcall;
var
ABmp: TBitmap;
Jpg: TJPEGImage;
MS: TMemoryStream;
BmpInfo: TBitmapInfo;
Hdb: Thandle;
begin
Result := 0;
ZeroMemory(@BmpInfo, SizeOf(BmpInfo));
ABmp := TBitmap.Create;
ABmp.Pixelformat := pf32bit;
SendMessage(hCapWnd, WM_CAP_GET_VIDEOFORMAT, wParam(SizeOf(BmpInfo)), LPARAM(@BmpInfo));
with ABmp do
begin
Width := BmpInfo.bmiHeader.biWidth;
Height := BmpInfo.bmiHeader.biHeight;
Hdb := DrawDibOpen;
DrawDibDraw(Hdb, Canvas.Handle, 0, 0, BmpInfo.BmiHeader.biWidth, BmpInfo.bmiHeader.biHeight, BmpInfo.bmiHeader, lpVHDR^.lpData, 0, 0, BmpInfo.bmiHeader.biWidth, BmpInfo.bmiHeader.biHeight, 0);
DrawDibClose(Hdb);
end;
try
MS := TMemoryStream.Create;
Jpg := TJPEGImage.Create;
Jpg.Assign(ABmp);
Jpg.CompressionQuality := 100;
Jpg.SaveToStream(MS);
MS.Position := 0;
Camera.Sock.SendText(IntToStr(MS.Size) + #0);
Camera.Sock.SendStream(MS);
finally
// Invalid Pointer Operation Exception :(
{MS.Free;
Jpg.Free;
ABmp.free;}
end;
end;
constructor TCamera.Create(Owner: TPanel; Socket: TCustomWinSocket);
begin
Sock := Socket;
try
VideoHwnd := capCreateCaptureWindowA('MyCamera', WS_CHILD or WS_VISIBLE, 0, 0, Owner.Width, Owner.Height, Owner.Handle, 0);
if (SendMessage(VideoHwnd, WM_CAP_DRIVER_CONNECT, 0, 0) <> 0) then
begin
SendMessage(VideoHwnd, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@capFrameCallback));
SendMessage(VideoHwnd, WM_CAP_SET_PREVIEW, -1, 0);
SendMessage(VideoHwnd, WM_CAP_SET_PREVIEWRATE, 100, 0);
SendMessage(VideoHwnd, WM_CAP_SET_SCALE, -1, 0);
Parent := Owner;
Owner.OnResize := Resize;
end;
except
exit;
end;
end;
destructor TCamera.Destroy;
begin
if (VideoHwnd <> 0) then
begin
SendMessage(VideoHwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
SetParent(VideoHwnd, 0);
SendMessage(VideoHwnd, WM_CLOSE, 0, 0);
end;
inherited;
end;
procedure TCamera.Resize(Sender: TObject);
begin
inherited;
if (VideoHwnd <> 0) then
begin
SetWindowPos(VideoHwnd, HWND_BOTTOM, 0, 0, Parent.Width, Parent.Height, SWP_NOMOVE or SWP_NOACTIVATE);
end;
end;
procedure TCamera.SetSize;
begin
SendMessage(VideoHwnd, WM_CAP_DLG_VIDEOFORMAT, 0, 0);
end;
procedure TCamera.SetSource;
begin
SendMessage(VideoHwnd, WM_CAP_DLG_VIDEOSOURCE, 0, 0);
end;
end.
uDrawDib.pas
unit uDrawDib;
interface
uses
Windows;
// DrawDib flags
const
DDF_UPDATE = $0002; // re-draw the last DIB
DDF_SAME_HDC = $0004; // HDC same as last call (all setup)
DDF_SAME_DRAW = $0008; // draw params are the same
DDF_DONTDRAW = $0010; // dont draw frame, just decompress
DDF_ANIMATE = $0020; // allow palette animation
DDF_BUFFER = $0040; // always buffer image
DDF_JUSTDRAWIT = $0080; // just draw it with GDI
DDF_FULLSCREEN = $0100; // use DisplayDib
DDF_BACKGROUNDPAL = $0200; // Realize palette in background
DDF_NOTKEYFRAME = $0400; // this is a partial frame update, hint
DDF_HURRYUP = $0800; // hurry up please!
DDF_HALFTONE = $1000; // always halftone
DDF_PREROLL = DDF_DONTDRAW; // Builing up a non-keyframe
DDF_SAME_DIB = DDF_SAME_DRAW;
DDF_SAME_SIZE = DDF_SAME_DRAW;
// display profiling
PD_CAN_DRAW_DIB = $0001; // if you can draw at all
PD_CAN_STRETCHDIB = $0002; // basicly RC_STRETCHDIB
PD_STRETCHDIB_1_1_OK = $0004; // is it fast?
PD_STRETCHDIB_1_2_OK = $0008;
PD_STRETCHDIB_1_N_OK = $0010;
//
WM_PALETTECHANGED = $311;
WM_QUERYNEWPALETTE = $30F;
type
hDrawDib = THandle;
TDrawDibTime = record
timeCount: LongInt; // see below
timeDraw: LongInt; // time to draw bitmaps
timeDecompress: LongInt; // time to decompress bitmaps
timeDither: LongInt; // time to dither bitmaps
timeStretch: LongInt; // time to stretch bitmaps
timeBlt: LongInt; // time to transfer bitmaps (BitBlt)
timeSetDIBits: LongInt; // time to transfer bitmaps (SetDIBits)
end;
function DrawDibBegin(hdd: hDrawDib; hDC: THandle; dxDest, dyDest: Integer;
var lpbi: TBitmapInfoHeader; dxSrc, dySrc: Integer; wFlags: UInt): Boolean; stdcall;
function DrawDibChangePalette(hdd: hDrawDib; iStart, iLen: Integer;
var lppe: TPaletteEntry): Boolean; stdcall;
function DrawDibClose(hdd: hDrawDib): Boolean; stdcall;
function DrawDibDraw(hdd: hDrawDib; hDC: THandle; xDst, yDst, dxDst, dyDst: Integer;
var lpbi: TBitmapInfoHeader; Bits: Pointer; xSrc, ySrc, dxSrc, dySrc: Integer;
wFlags: UInt): Boolean; stdcall;
function DrawDibEnd(hdd: hDrawDib): Boolean; stdcall;
function DrawDibGetBuffer(hdd: hDrawDib; var lpbi: TBitmapInfoHeader;
dwSize, dwFlags: DWord): Pointer; stdcall;
function DrawDibGetPalette(hdd: hDrawDib): THandle; stdcall;
function DrawDibOpen: hDrawDib; stdcall;
function DrawDibProfileDisplay(var lpbi: TBitmapInfoHeader): Boolean; stdcall;
function DrawDibRealize(hdd: hDrawDib; hDC: THandle; fBackground: Bool): UInt; stdcall;
function DrawDibSetPalette(hdd: hDrawDib; hpal: Pointer): Boolean; stdcall;
function DrawDibStart(hdd: hDrawDib; rate: LongInt): Boolean; stdcall;
function DrawDibStop(hdd: hDrawDib): Boolean; stdcall;
function DrawDibTime(hdd: hDrawDib; var lpddtime: TDrawDibTime): Boolean; stdcall;
implementation
const
DLL = 'MsVfW32.dll';
function DrawDibBegin; external DLL name 'DrawDibBegin';
function DrawDibChangePalette; external DLL name 'DrawDibChangePalette';
function DrawDibClose; external DLL name 'DrawDibClose';
function DrawDibDraw; external DLL name 'DrawDibDraw';
function DrawDibEnd; external DLL name 'DrawDibEnd';
function DrawDibGetBuffer; external DLL name 'DrawDibGetBuffer';
function DrawDibGetPalette; external DLL name 'DrawDibGetPalette';
function DrawDibOpen; external DLL name 'DrawDibOpen';
function DrawDibProfileDisplay; external DLL name 'DrawDibProfileDisplay';
function DrawDibRealize; external DLL name 'DrawDibRealize';
function DrawDibSetPalette; external DLL name 'DrawDibSetPalette';
function DrawDibStart; external DLL name 'DrawDibStart';
function DrawDibStop; external DLL name 'DrawDibStop';
function DrawDibTime; external DLL name 'DrawDibTime';
end.
来源:https://stackoverflow.com/questions/57533637/how-send-receive-frames-of-a-live-webcam-solved