Delphi - 使用 IdCmdTCPServer 向客户端发送流
Delphi - Send stream using IdCmdTCPServer to a Client
我正在尝试使用 Indy 10 将从 android 相机拍摄的 jpg 图像流发送到客户端,我从 Delphi 获得了示例 CameraComponent,它从相机获取图像并在 TImage 中显示,我想做的是使用 IdTCPClient 将此流发送到客户端。
我正在使用 IdCmdTCPServer 发送流,客户端需要数据,但是当我 运行 我 android (Galaxy S4 mini) 上的服务器应用程序 运行太慢了,相机更新显示的图像很慢,我可以连接到服务器,但只发送了一张图像,然后服务器应用程序停止响应。
我认为我的问题与多线程有关,但我不知道如何解决它。这是我的代码。
unit uMain;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, FMX.Media,
FMX.Platform, FMX.Objects, FMX.Layouts, FMX.Memo,FMX.Controls.Presentation,
System.Generics.Collections,
System.IOUtils, IdCmdTCPServer,
IdCommandHandlers, IdContext, IdStack, IdBaseComponent, IdComponent,
IdCustomTCPServer, IdTCPServer, FMX.ScrollBox, IdIOHandler, IdIOHandlerStream,
IdCustomHTTPServer, IdHTTPServer, IdUDPBase, IdUDPServer, IdTCPConnection,
IdSimpleServer;
type
TCameraComponentForm = class(TForm)
CameraComponent1: TCameraComponent;
btnStartCamera: TButton;
imgCameraView: TImage;
btnFrontCamera: TSpeedButton;
btnBackCamera: TSpeedButton;
Memo1: TMemo;
IdCmdTCPServer1: TIdCmdTCPServer;
procedure btnStartCameraClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure CameraComponent1SampleBufferReady(Sender: TObject;
const ATime: TMediaTime);
procedure btnFrontCameraClick(Sender: TObject);
procedure btnBackCameraClick(Sender: TObject);
procedure IdCmdTCPServer1Connect(AContext: TIdContext);
procedure IdCmdTCPServer1Disconnect(AContext: TIdContext);
procedure IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
private
{ Private declarations }
imag: TMemoryStream;
Enable_Stream: Boolean;
Camera_enable: Boolean;
procedure GetImage;
procedure SendStream;
public
{ Public declarations }
function AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
end;
var
CameraComponentForm: TCameraComponentForm;
implementation
{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID}
procedure TCameraComponentForm.FormCreate(Sender: TObject);
var
AppEventSvc: IFMXApplicationEventService;
begin
Camera_enable:= False;
// Stream to be sent
imag:= TMemoryStream.Create;
Enable_Stream:= False;
// Start server
IdCmdTCPServer1.Active:= True;
{ by default, we start with Front Camera and Flash Off }
CameraComponent1.Kind := FMX.Media.TCameraKind.ckFrontCamera;
if CameraComponent1.HasFlash then
CameraComponent1.FlashMode := FMX.Media.TFlashMode.fmFlashOff;
CameraComponent1.CaptureSettingPriority := TVideoCaptureSettingPriority.FrameRate;
{ Add platform service to see camera state. }
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
AppEventSvc.SetApplicationEventHandler(AppEvent);
end;
procedure TCameraComponentForm.Timer1Timer(Sender: TObject);
begin
imgCameraView.Repaint;
end;
{ Make sure the camera is released if you're going away.}
function TCameraComponentForm.AppEvent(AAppEvent: TApplicationEvent;
AContext: TObject): Boolean;
begin
case AAppEvent of
TApplicationEvent.WillBecomeInactive:
CameraComponent1.Active := False;
TApplicationEvent.EnteredBackground:
CameraComponent1.Active := False;
TApplicationEvent.WillTerminate:
CameraComponent1.Active := False;
end;
end;
procedure TCameraComponentForm.btnBackCameraClick(Sender: TObject);
begin
{ select Back Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.BackCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnFrontCameraClick(Sender: TObject);
begin
{ select Front Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.FrontCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnStartCameraClick(Sender: TObject);
begin
if Camera_enable = False then
begin
Camera_enable:= True;
{ turn on the Camera }
CameraComponent1.Active := True;
end
else
begin
Camera_enable:= False;
{ turn off the Camera }
CameraComponent1.Active := False;
end;
end;
procedure TCameraComponentForm.CameraComponent1SampleBufferReady(
Sender: TObject; const ATime: TMediaTime);
begin
// Update the TImage
TThread.Synchronize(TThread.CurrentThread, GetImage);
// Save the bitmap to stream and send to client
imgCameraView.Bitmap.SaveToStream(imag);
if Enable_Stream then
SendStream;
//imgCameraView.Width := imgCameraView.Bitmap.Width;
//imgCameraView.Height := imgCameraView.Bitmap.Height;
end;
procedure TCameraComponentForm.GetImage;
begin
CameraComponent1.SampleBufferToBitmap(imgCameraView.Bitmap, True);
end;
procedure TCameraComponentForm.IdCmdTCPServer1CommandHandlers0Command(
ASender: TIdCommand);
begin
Memo1.Lines.Add('Send Stream');
Enable_Stream:= True;
end;
procedure TCameraComponentForm.IdCmdTCPServer1Connect(AContext: TIdContext);
begin
Memo1.Lines.Add('Connection being made - '+ AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TCameraComponentForm.IdCmdTCPServer1Disconnect(AContext: TIdContext);
begin
Memo1.Lines.Add('Disconnection being made - '+ AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TCameraComponentForm.SendStream;
var
index: integer;
begin
// Write to the client in a thread safe way
with IdCmdTCPServer1.Contexts.LockList do
try
for index := 0 to Count - 1 do
begin
TIdContext( Items[index] ).Connection.IOHandler.WriteLn('Stream');
TIdContext( Items[index] ).Connection.IOHandler.Write(imag,0,True);
end;
finally
IdCmdTCPServer1.Contexts.UnlockList;
end;
end;
end.
我认为 CameraComponent 和 Server 的线程不同步,但我不知道如何解决它并加快应用程序。
感谢任何帮助。
TIdCmdTCPServer
是一个多线程组件。 OnConnect
、OnDisconnect
和 OnCommand
事件在为连接的客户端创建的工作线程的上下文中触发。您对这些事件的处理程序没有使用线程安全代码,并且您正在主 UI 线程而不是客户端工作线程的上下文中执行套接字 I/O。
但是,TIdCmdTCPServer
客户端工作线程通常在客户端未发送命令时被阻塞,并且它本身不允许您在该空闲时间注入自己的 I/O 代码。因此,您必须发挥一点创意,让客户端线程检查 TImage
是否有新图像并在不阻塞主 UI 线程的情况下发送它们。
尝试这样的事情:
unit uMain;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, FMX.Media,
FMX.Platform, FMX.Objects, FMX.Layouts, FMX.Memo, FMX.ScrollBox, FMX.Controls.Presentation,
System.Generics.Collections,
System.IOUtils, IdGlobal, IdCmdTCPServer,
IdCommandHandlers, IdContext, IdStack, IdBaseComponent, IdComponent,
IdCustomTCPServer, IdTCPServer, IdTCPConnection, IdIOHandler;
type
TIdCmdTCPServer = class(IdCmdTCPServer.TIdCmdTCPServer)
protected
procedure InitComponent; override;
procedure DoExecute(AContext: TIdContext): Boolean; override;
end;
TCameraComponentForm = class(TForm)
CameraComponent1: TCameraComponent;
btnStartCamera: TButton;
imgCameraView: TImage;
btnFrontCamera: TSpeedButton;
btnBackCamera: TSpeedButton;
Memo1: TMemo;
IdCmdTCPServer1: TIdCmdTCPServer;
procedure btnStartCameraClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure CameraComponent1SampleBufferReady(Sender: TObject;
const ATime: TMediaTime);
procedure btnFrontCameraClick(Sender: TObject);
procedure btnBackCameraClick(Sender: TObject);
procedure IdCmdTCPServer1Connect(AContext: TIdContext);
procedure IdCmdTCPServer1Disconnect(AContext: TIdContext);
procedure IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
private
{ Private declarations }
Enable_Stream: Boolean;
Image_Updated: TIdTicks;
procedure GetImage;
public
{ Public declarations }
function AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
end;
var
CameraComponentForm: TCameraComponentForm;
implementation
{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID}
uses
IdYarn;
type
TMyContext = class(TIdServerContext)
public
LastUpdate: TIdTicks;
Img: TMemoryStream;
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
destructor Destroy; override;
end;
procedure TCameraComponentForm.FormCreate(Sender: TObject);
var
AppEventSvc: IFMXApplicationEventService;
begin
Enable_Stream := False;
Image_Updated := 0;
{ by default, we start with Front Camera and Flash Off }
CameraComponent1.Kind := FMX.Media.TCameraKind.ckFrontCamera;
if CameraComponent1.HasFlash then
CameraComponent1.FlashMode := FMX.Media.TFlashMode.fmFlashOff;
CameraComponent1.CaptureSettingPriority := TVideoCaptureSettingPriority.FrameRate;
{ Add platform service to see camera state. }
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
AppEventSvc.SetApplicationEventHandler(AppEvent);
// Start server
IdCmdTCPServer1.Active := True;
end;
{ Make sure the camera is released if you're going away.}
function TCameraComponentForm.AppEvent(AAppEvent: TApplicationEvent;
AContext: TObject): Boolean;
begin
case AAppEvent of
TApplicationEvent.WillBecomeInactive:
CameraComponent1.Active := False;
TApplicationEvent.EnteredBackground:
CameraComponent1.Active := False;
TApplicationEvent.WillTerminate:
CameraComponent1.Active := False;
end;
end;
procedure TCameraComponentForm.btnBackCameraClick(Sender: TObject);
begin
{ select Back Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.BackCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnFrontCameraClick(Sender: TObject);
begin
{ select Front Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.FrontCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnStartCameraClick(Sender: TObject);
begin
{ turn on/off the Camera }
CameraComponent1.Active := not CameraComponent1.Active;
end;
procedure TCameraComponentForm.CameraComponent1SampleBufferReady(
Sender: TObject; const ATime: TMediaTime);
begin
// Update the TImage. Call GetImage() only once to get the
// latest sample buffer in case this event is triggered
// multiple times before GetImage() is called...
TThread.RemoveQueuedEvents(nil, GetImage);
TThread.Queue(nil, GetImage);
end;
procedure TCameraComponentForm.GetImage;
begin
CameraComponent1.SampleBufferToBitmap(imgCameraView.Bitmap, True);
imgCameraView.Repaint;
Image_Updated := Ticks64;
//imgCameraView.Width := imgCameraView.Bitmap.Width;
//imgCameraView.Height := imgCameraView.Bitmap.Height;
end;
procedure TCameraComponentForm.IdCmdTCPServer1CommandHandlers0Command(
ASender: TIdCommand);
begin
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add('Send Stream');
end
);
Enable_Stream := True;
end;
procedure TCameraComponentForm.IdCmdTCPServer1Connect(AContext: TIdContext);
var
str: string;
begin
str := 'Connection being made - '+ AContext.Binding.PeerIP;
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(str);
end
);
end;
procedure TCameraComponentForm.IdCmdTCPServer1Disconnect(AContext: TIdContext);
var
str: string;
begin
str := 'Disconnection being made - '+ AContext.Binding.PeerIP;
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(str);
end
);
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
inherited Create(AConnection, AYarn, AList);
Img := TMemoryStream.Create;
end;
destructor TMyContext.Destroy;
begin
Img.Free;
inherited Destroy;
end;
procedure TIdCmdTCPServer.InitComponent;
begin
inherited InitComponent;
ContextClass := TMyContext;
end;
procedure TIdCmdTCPServer.DoExecute(AContext: TIdContext): Boolean;
var
LContext: TMyContext;
LTicks: TIdTicks;
begin
Result := True;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(10);
AContext.Connection.IOHandler.CheckForDisconnect;
end;
if not LContext.Connection.IOHandler.InputBufferIsEmpty then
begin
Result := inherited DoExecute(AContext); // process a pending command
if not Result then Exit; // disconnected
end;
if not Enable_Stream then Exit;
LContext := TMyContext(AContext);
LTicks := Image_Updated;
if LContext.LastUpdate = LTicks then Exit;
LContext.LastUpdate := LTicks;
LContext.Img.Clear;
TThread.Synchronize(nil,
procedure
begin
CameraComponentForm.imgCameraView.Bitmap.SaveToStream(LContext.Img);
end
);
AContext.Connection.IOHandler.WriteLn('Stream');
AContext.Connection.IOHandler.Write(LContext.Img, 0, True);
Result := AContext.Connection.Connected;
end;
end.
我正在尝试使用 Indy 10 将从 android 相机拍摄的 jpg 图像流发送到客户端,我从 Delphi 获得了示例 CameraComponent,它从相机获取图像并在 TImage 中显示,我想做的是使用 IdTCPClient 将此流发送到客户端。
我正在使用 IdCmdTCPServer 发送流,客户端需要数据,但是当我 运行 我 android (Galaxy S4 mini) 上的服务器应用程序 运行太慢了,相机更新显示的图像很慢,我可以连接到服务器,但只发送了一张图像,然后服务器应用程序停止响应。
我认为我的问题与多线程有关,但我不知道如何解决它。这是我的代码。
unit uMain;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, FMX.Media,
FMX.Platform, FMX.Objects, FMX.Layouts, FMX.Memo,FMX.Controls.Presentation,
System.Generics.Collections,
System.IOUtils, IdCmdTCPServer,
IdCommandHandlers, IdContext, IdStack, IdBaseComponent, IdComponent,
IdCustomTCPServer, IdTCPServer, FMX.ScrollBox, IdIOHandler, IdIOHandlerStream,
IdCustomHTTPServer, IdHTTPServer, IdUDPBase, IdUDPServer, IdTCPConnection,
IdSimpleServer;
type
TCameraComponentForm = class(TForm)
CameraComponent1: TCameraComponent;
btnStartCamera: TButton;
imgCameraView: TImage;
btnFrontCamera: TSpeedButton;
btnBackCamera: TSpeedButton;
Memo1: TMemo;
IdCmdTCPServer1: TIdCmdTCPServer;
procedure btnStartCameraClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure CameraComponent1SampleBufferReady(Sender: TObject;
const ATime: TMediaTime);
procedure btnFrontCameraClick(Sender: TObject);
procedure btnBackCameraClick(Sender: TObject);
procedure IdCmdTCPServer1Connect(AContext: TIdContext);
procedure IdCmdTCPServer1Disconnect(AContext: TIdContext);
procedure IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
private
{ Private declarations }
imag: TMemoryStream;
Enable_Stream: Boolean;
Camera_enable: Boolean;
procedure GetImage;
procedure SendStream;
public
{ Public declarations }
function AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
end;
var
CameraComponentForm: TCameraComponentForm;
implementation
{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID}
procedure TCameraComponentForm.FormCreate(Sender: TObject);
var
AppEventSvc: IFMXApplicationEventService;
begin
Camera_enable:= False;
// Stream to be sent
imag:= TMemoryStream.Create;
Enable_Stream:= False;
// Start server
IdCmdTCPServer1.Active:= True;
{ by default, we start with Front Camera and Flash Off }
CameraComponent1.Kind := FMX.Media.TCameraKind.ckFrontCamera;
if CameraComponent1.HasFlash then
CameraComponent1.FlashMode := FMX.Media.TFlashMode.fmFlashOff;
CameraComponent1.CaptureSettingPriority := TVideoCaptureSettingPriority.FrameRate;
{ Add platform service to see camera state. }
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
AppEventSvc.SetApplicationEventHandler(AppEvent);
end;
procedure TCameraComponentForm.Timer1Timer(Sender: TObject);
begin
imgCameraView.Repaint;
end;
{ Make sure the camera is released if you're going away.}
function TCameraComponentForm.AppEvent(AAppEvent: TApplicationEvent;
AContext: TObject): Boolean;
begin
case AAppEvent of
TApplicationEvent.WillBecomeInactive:
CameraComponent1.Active := False;
TApplicationEvent.EnteredBackground:
CameraComponent1.Active := False;
TApplicationEvent.WillTerminate:
CameraComponent1.Active := False;
end;
end;
procedure TCameraComponentForm.btnBackCameraClick(Sender: TObject);
begin
{ select Back Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.BackCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnFrontCameraClick(Sender: TObject);
begin
{ select Front Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.FrontCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnStartCameraClick(Sender: TObject);
begin
if Camera_enable = False then
begin
Camera_enable:= True;
{ turn on the Camera }
CameraComponent1.Active := True;
end
else
begin
Camera_enable:= False;
{ turn off the Camera }
CameraComponent1.Active := False;
end;
end;
procedure TCameraComponentForm.CameraComponent1SampleBufferReady(
Sender: TObject; const ATime: TMediaTime);
begin
// Update the TImage
TThread.Synchronize(TThread.CurrentThread, GetImage);
// Save the bitmap to stream and send to client
imgCameraView.Bitmap.SaveToStream(imag);
if Enable_Stream then
SendStream;
//imgCameraView.Width := imgCameraView.Bitmap.Width;
//imgCameraView.Height := imgCameraView.Bitmap.Height;
end;
procedure TCameraComponentForm.GetImage;
begin
CameraComponent1.SampleBufferToBitmap(imgCameraView.Bitmap, True);
end;
procedure TCameraComponentForm.IdCmdTCPServer1CommandHandlers0Command(
ASender: TIdCommand);
begin
Memo1.Lines.Add('Send Stream');
Enable_Stream:= True;
end;
procedure TCameraComponentForm.IdCmdTCPServer1Connect(AContext: TIdContext);
begin
Memo1.Lines.Add('Connection being made - '+ AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TCameraComponentForm.IdCmdTCPServer1Disconnect(AContext: TIdContext);
begin
Memo1.Lines.Add('Disconnection being made - '+ AContext.Connection.Socket.Binding.PeerIP);
end;
procedure TCameraComponentForm.SendStream;
var
index: integer;
begin
// Write to the client in a thread safe way
with IdCmdTCPServer1.Contexts.LockList do
try
for index := 0 to Count - 1 do
begin
TIdContext( Items[index] ).Connection.IOHandler.WriteLn('Stream');
TIdContext( Items[index] ).Connection.IOHandler.Write(imag,0,True);
end;
finally
IdCmdTCPServer1.Contexts.UnlockList;
end;
end;
end.
我认为 CameraComponent 和 Server 的线程不同步,但我不知道如何解决它并加快应用程序。
感谢任何帮助。
TIdCmdTCPServer
是一个多线程组件。 OnConnect
、OnDisconnect
和 OnCommand
事件在为连接的客户端创建的工作线程的上下文中触发。您对这些事件的处理程序没有使用线程安全代码,并且您正在主 UI 线程而不是客户端工作线程的上下文中执行套接字 I/O。
但是,TIdCmdTCPServer
客户端工作线程通常在客户端未发送命令时被阻塞,并且它本身不允许您在该空闲时间注入自己的 I/O 代码。因此,您必须发挥一点创意,让客户端线程检查 TImage
是否有新图像并在不阻塞主 UI 线程的情况下发送它们。
尝试这样的事情:
unit uMain;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, FMX.Media,
FMX.Platform, FMX.Objects, FMX.Layouts, FMX.Memo, FMX.ScrollBox, FMX.Controls.Presentation,
System.Generics.Collections,
System.IOUtils, IdGlobal, IdCmdTCPServer,
IdCommandHandlers, IdContext, IdStack, IdBaseComponent, IdComponent,
IdCustomTCPServer, IdTCPServer, IdTCPConnection, IdIOHandler;
type
TIdCmdTCPServer = class(IdCmdTCPServer.TIdCmdTCPServer)
protected
procedure InitComponent; override;
procedure DoExecute(AContext: TIdContext): Boolean; override;
end;
TCameraComponentForm = class(TForm)
CameraComponent1: TCameraComponent;
btnStartCamera: TButton;
imgCameraView: TImage;
btnFrontCamera: TSpeedButton;
btnBackCamera: TSpeedButton;
Memo1: TMemo;
IdCmdTCPServer1: TIdCmdTCPServer;
procedure btnStartCameraClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure CameraComponent1SampleBufferReady(Sender: TObject;
const ATime: TMediaTime);
procedure btnFrontCameraClick(Sender: TObject);
procedure btnBackCameraClick(Sender: TObject);
procedure IdCmdTCPServer1Connect(AContext: TIdContext);
procedure IdCmdTCPServer1Disconnect(AContext: TIdContext);
procedure IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
private
{ Private declarations }
Enable_Stream: Boolean;
Image_Updated: TIdTicks;
procedure GetImage;
public
{ Public declarations }
function AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
end;
var
CameraComponentForm: TCameraComponentForm;
implementation
{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID}
uses
IdYarn;
type
TMyContext = class(TIdServerContext)
public
LastUpdate: TIdTicks;
Img: TMemoryStream;
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
destructor Destroy; override;
end;
procedure TCameraComponentForm.FormCreate(Sender: TObject);
var
AppEventSvc: IFMXApplicationEventService;
begin
Enable_Stream := False;
Image_Updated := 0;
{ by default, we start with Front Camera and Flash Off }
CameraComponent1.Kind := FMX.Media.TCameraKind.ckFrontCamera;
if CameraComponent1.HasFlash then
CameraComponent1.FlashMode := FMX.Media.TFlashMode.fmFlashOff;
CameraComponent1.CaptureSettingPriority := TVideoCaptureSettingPriority.FrameRate;
{ Add platform service to see camera state. }
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
AppEventSvc.SetApplicationEventHandler(AppEvent);
// Start server
IdCmdTCPServer1.Active := True;
end;
{ Make sure the camera is released if you're going away.}
function TCameraComponentForm.AppEvent(AAppEvent: TApplicationEvent;
AContext: TObject): Boolean;
begin
case AAppEvent of
TApplicationEvent.WillBecomeInactive:
CameraComponent1.Active := False;
TApplicationEvent.EnteredBackground:
CameraComponent1.Active := False;
TApplicationEvent.WillTerminate:
CameraComponent1.Active := False;
end;
end;
procedure TCameraComponentForm.btnBackCameraClick(Sender: TObject);
begin
{ select Back Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.BackCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnFrontCameraClick(Sender: TObject);
begin
{ select Front Camera }
CameraComponent1.Active := False;
CameraComponent1.Kind := FMX.Media.TCameraKind.FrontCamera;
CameraComponent1.Active := True;
end;
procedure TCameraComponentForm.btnStartCameraClick(Sender: TObject);
begin
{ turn on/off the Camera }
CameraComponent1.Active := not CameraComponent1.Active;
end;
procedure TCameraComponentForm.CameraComponent1SampleBufferReady(
Sender: TObject; const ATime: TMediaTime);
begin
// Update the TImage. Call GetImage() only once to get the
// latest sample buffer in case this event is triggered
// multiple times before GetImage() is called...
TThread.RemoveQueuedEvents(nil, GetImage);
TThread.Queue(nil, GetImage);
end;
procedure TCameraComponentForm.GetImage;
begin
CameraComponent1.SampleBufferToBitmap(imgCameraView.Bitmap, True);
imgCameraView.Repaint;
Image_Updated := Ticks64;
//imgCameraView.Width := imgCameraView.Bitmap.Width;
//imgCameraView.Height := imgCameraView.Bitmap.Height;
end;
procedure TCameraComponentForm.IdCmdTCPServer1CommandHandlers0Command(
ASender: TIdCommand);
begin
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add('Send Stream');
end
);
Enable_Stream := True;
end;
procedure TCameraComponentForm.IdCmdTCPServer1Connect(AContext: TIdContext);
var
str: string;
begin
str := 'Connection being made - '+ AContext.Binding.PeerIP;
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(str);
end
);
end;
procedure TCameraComponentForm.IdCmdTCPServer1Disconnect(AContext: TIdContext);
var
str: string;
begin
str := 'Disconnection being made - '+ AContext.Binding.PeerIP;
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(str);
end
);
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
inherited Create(AConnection, AYarn, AList);
Img := TMemoryStream.Create;
end;
destructor TMyContext.Destroy;
begin
Img.Free;
inherited Destroy;
end;
procedure TIdCmdTCPServer.InitComponent;
begin
inherited InitComponent;
ContextClass := TMyContext;
end;
procedure TIdCmdTCPServer.DoExecute(AContext: TIdContext): Boolean;
var
LContext: TMyContext;
LTicks: TIdTicks;
begin
Result := True;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(10);
AContext.Connection.IOHandler.CheckForDisconnect;
end;
if not LContext.Connection.IOHandler.InputBufferIsEmpty then
begin
Result := inherited DoExecute(AContext); // process a pending command
if not Result then Exit; // disconnected
end;
if not Enable_Stream then Exit;
LContext := TMyContext(AContext);
LTicks := Image_Updated;
if LContext.LastUpdate = LTicks then Exit;
LContext.LastUpdate := LTicks;
LContext.Img.Clear;
TThread.Synchronize(nil,
procedure
begin
CameraComponentForm.imgCameraView.Bitmap.SaveToStream(LContext.Img);
end
);
AContext.Connection.IOHandler.WriteLn('Stream');
AContext.Connection.IOHandler.Write(LContext.Img, 0, True);
Result := AContext.Connection.Connected;
end;
end.