TCameraComponent 和 TVideoCaptureDevice 不在 Win32 中初始化
TCameraComponent and TVideoCaptureDevice do not initialize in Win32
我使用标准代码初始化 TVideoCaptureDevice 并开始捕获。
const M_LAUNCH_CAMERA = WM_APP + 450;
type
TCamSF1 = class(TForm)
...
protected
procedure LaunchCamera(var Message: TMessage); message M_LAUNCH_CAMERA;
...
end;
...
procedure TCamSF1.LaunchCamera(var Message: TMessage);
begin
if VideoCamera = nil then
begin
VideoCamera := TCaptureDeviceManager.Current.DefaultVideoCaptureDevice;
if VideoCamera <> nil then
begin
VideoCamera.OnSampleBufferReady := CameraReady;
VideoCamera.StartCapture;
end
else
begin
Caption := 'Video capture devices not available.';
end;
end
else
begin
VideoCamera.StartCapture;
end;
end;
procedure TCamSF1.IdTCPServer1Execute(AContext: TIdContext);
var
S: AnsiString;
Command: TAnsiStrings;
Msg: TMessage;
begin
if (AContext <> nil) and (AContext.Connection.Socket.Connected) and
(not AContext.Connection.Socket.InputBufferIsEmpty) then
S := AContext.Connection.Socket.ReadLn;
if S = '' then
exit;
Memo1.Lines.Add(S);
Command := ParseCommandString(S, '#');
if Command[0] = 'camresol' then
begin
CamShotParams := Command;
Msg.Msg := M_LAUNCH_CAMERA;
Dispatch(Msg);
end;
end;
当我从按钮 OnClick 事件发送消息时代码正常工作,但是当从 TIdTCPServer OnExecute 发送消息时,相机不会启动并且 Caption := 'Video capture devices not available.'
是 运行。此外,此后相机甚至不会从 Button OnClick 事件初始化。
如果直接调用
,代码也不起作用
VideoCamera := TCaptureDeviceManager.Current.DefaultVideoCaptureDevice;
if VideoCamera <> nil then
begin
VideoCamera.OnSampleBufferReady := CameraReady;
VideoCamera.StartCapture;
end;
来自 Server OnExecute 事件。虽然当 运行 来自 Button OnClick 时它工作正常。
使用 TCameraComponent 会导致同样的问题。
如果在 Form OnCreate 事件中处理相机初始化,则可以解决此问题,但这不适合,因为两个或多个应用程序不允许同时使用相机。
看来,捕获设备应该从主线程初始化和操作。尝试在 TThread.Synchronize class 过程中包装捕获操作,如下所示:
procedure TMyForm.IdTCPServer1Execute(AContext: TIdContext);
...
begin
...
TThread.Synchronize(nil,
procedure
begin
DoSmthWithCamera();
end;
);
...
end;
为什么从TIdTCPServer.OnExecute
初始化相机不起作用是因为OnExecute事件方法中的代码默认在单独的线程中执行。
所以你遇到了在多线程应用程序中访问VCL的常见问题。
您应该确保您的相机初始化和完成代码是通过同步从主线程执行的。
感谢您的帮助,特别感谢@whosrdaddy、@SilverWarior 和@Sergey-Krasilnikov。
我找到了出路,虽然它看起来不太好。我决定使用 TTimer。它有以下 OnTimer 事件。
procedure TCamSF1.Timer1Timer(Sender: TObject);
begin
if IdTCPServer1.Contexts.IsCountLessThan(1) then
begin
if (CameraComponent <> nil) and (CameraComponent.Active) then
CameraComponent.Active := false;
if CameraComponent <> nil then
begin
CameraComponent.Destroy;
CameraComponent.FreeOnRelease;
CameraComponent := nil;
end;
end
else
begin
if CameraComponent = nil then
begin
CameraComponent := TCameraComponent.Create(Self);
CameraComponent.OnSampleBufferReady := CameraComponentReady;
end;
CameraComponent.Active := true;
end;
end;
所以我设法通过 connecting/disconnecting 客户端切换相机 on/off。如果您找到更好的解决方案,请告诉我。
如果按以下方式调用调度,代码将正常工作:
procedure TCamSF1.IdTCPServer1Execute(AContext: TIdContext);
var
Command: TAnsiStrings;
Msg: TMessage;
begin
...
if ... then
begin
TThread.Synchronize(TThread.CurrentThread, (
procedure
begin
Counter := 0;
CamShotParams := Command;
Msg.Msg := M_LAUNCH_CAMERA;
Dispatch(Msg)
end));
end;
end;
我使用标准代码初始化 TVideoCaptureDevice 并开始捕获。
const M_LAUNCH_CAMERA = WM_APP + 450;
type
TCamSF1 = class(TForm)
...
protected
procedure LaunchCamera(var Message: TMessage); message M_LAUNCH_CAMERA;
...
end;
...
procedure TCamSF1.LaunchCamera(var Message: TMessage);
begin
if VideoCamera = nil then
begin
VideoCamera := TCaptureDeviceManager.Current.DefaultVideoCaptureDevice;
if VideoCamera <> nil then
begin
VideoCamera.OnSampleBufferReady := CameraReady;
VideoCamera.StartCapture;
end
else
begin
Caption := 'Video capture devices not available.';
end;
end
else
begin
VideoCamera.StartCapture;
end;
end;
procedure TCamSF1.IdTCPServer1Execute(AContext: TIdContext);
var
S: AnsiString;
Command: TAnsiStrings;
Msg: TMessage;
begin
if (AContext <> nil) and (AContext.Connection.Socket.Connected) and
(not AContext.Connection.Socket.InputBufferIsEmpty) then
S := AContext.Connection.Socket.ReadLn;
if S = '' then
exit;
Memo1.Lines.Add(S);
Command := ParseCommandString(S, '#');
if Command[0] = 'camresol' then
begin
CamShotParams := Command;
Msg.Msg := M_LAUNCH_CAMERA;
Dispatch(Msg);
end;
end;
当我从按钮 OnClick 事件发送消息时代码正常工作,但是当从 TIdTCPServer OnExecute 发送消息时,相机不会启动并且 Caption := 'Video capture devices not available.'
是 运行。此外,此后相机甚至不会从 Button OnClick 事件初始化。
如果直接调用
,代码也不起作用VideoCamera := TCaptureDeviceManager.Current.DefaultVideoCaptureDevice;
if VideoCamera <> nil then
begin
VideoCamera.OnSampleBufferReady := CameraReady;
VideoCamera.StartCapture;
end;
来自 Server OnExecute 事件。虽然当 运行 来自 Button OnClick 时它工作正常。 使用 TCameraComponent 会导致同样的问题。 如果在 Form OnCreate 事件中处理相机初始化,则可以解决此问题,但这不适合,因为两个或多个应用程序不允许同时使用相机。
看来,捕获设备应该从主线程初始化和操作。尝试在 TThread.Synchronize class 过程中包装捕获操作,如下所示:
procedure TMyForm.IdTCPServer1Execute(AContext: TIdContext);
...
begin
...
TThread.Synchronize(nil,
procedure
begin
DoSmthWithCamera();
end;
);
...
end;
为什么从TIdTCPServer.OnExecute
初始化相机不起作用是因为OnExecute事件方法中的代码默认在单独的线程中执行。
所以你遇到了在多线程应用程序中访问VCL的常见问题。
您应该确保您的相机初始化和完成代码是通过同步从主线程执行的。
感谢您的帮助,特别感谢@whosrdaddy、@SilverWarior 和@Sergey-Krasilnikov。 我找到了出路,虽然它看起来不太好。我决定使用 TTimer。它有以下 OnTimer 事件。
procedure TCamSF1.Timer1Timer(Sender: TObject);
begin
if IdTCPServer1.Contexts.IsCountLessThan(1) then
begin
if (CameraComponent <> nil) and (CameraComponent.Active) then
CameraComponent.Active := false;
if CameraComponent <> nil then
begin
CameraComponent.Destroy;
CameraComponent.FreeOnRelease;
CameraComponent := nil;
end;
end
else
begin
if CameraComponent = nil then
begin
CameraComponent := TCameraComponent.Create(Self);
CameraComponent.OnSampleBufferReady := CameraComponentReady;
end;
CameraComponent.Active := true;
end;
end;
所以我设法通过 connecting/disconnecting 客户端切换相机 on/off。如果您找到更好的解决方案,请告诉我。
如果按以下方式调用调度,代码将正常工作:
procedure TCamSF1.IdTCPServer1Execute(AContext: TIdContext);
var
Command: TAnsiStrings;
Msg: TMessage;
begin
...
if ... then
begin
TThread.Synchronize(TThread.CurrentThread, (
procedure
begin
Counter := 0;
CamShotParams := Command;
Msg.Msg := M_LAUNCH_CAMERA;
Dispatch(Msg)
end));
end;
end;