You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何停止独立线程中运行的摄像头?Camera.Destroy无效问题解决

解决独立线程中摄像头无法停止的问题

你使用TCamera类在独立线程中启动摄像头后,调用Camera.Destroy无法正常停止摄像头,核心问题在于线程生命周期未管理、摄像头未先停止捕获,以及跨线程VCL组件操作的风险。以下是具体修复方案:

问题分析

  1. 线程引用丢失:创建TCameraThread后未保存对象引用,无法控制线程的终止,也无法确保线程安全销毁。
  2. 摄像头未先停止捕获:直接调用Destroy前,未调用TCamera的停止方法(比如StopCapture,需根据Webcam单元的API调整),导致摄像头硬件仍在工作。
  3. 跨线程操作VCL组件:在非主线程中创建TCamera并关联Panel1,VCL组件仅允许在主线程访问,可能引发未知错误。

修正后的代码

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Panel1: TPanel;
    Image1: TImage; // 用于加载摄像头Bitmap流
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    FCameraThread: TCameraThread;
    procedure UpdateCameraImage(const ABitmap: TBitmap);
  public
    { Public declarations }
  end;

type
  TCameraThread = class(TThread)
  private
    FPanel: TPanel;
    FOnImageReady: TProc<TBitmap>;
    FCamera: TCamera;
  protected
    procedure Execute; override;
  public
    constructor Create(APanel: TPanel; AOnImageReady: TProc<TBitmap>);
    destructor Destroy; override;
    procedure StopCamera;
  end;

var
  Form1: TForm1;

implementation

uses
  Webcam, Vcl.Graphics;

{$R *.dfm}

{ TCameraThread }

constructor TCameraThread.Create(APanel: TPanel; AOnImageReady: TProc<TBitmap>);
begin
  inherited Create(True); // 先挂起线程,手动启动
  FPanel := APanel;
  FOnImageReady := AOnImageReady;
  FreeOnTerminate := False; // 手动管理销毁
end;

destructor TCameraThread.Destroy;
begin
  StopCamera;
  inherited;
end;

procedure TCameraThread.Execute;
var
  CaptureBitmap: TBitmap;
begin
  // 主线程创建关联VCL的Camera
  TThread.Synchronize(nil, procedure
  begin
    FCamera := TCamera.Create(FPanel);
    FCamera.StartCapture; // 调用摄像头启动捕获的方法
  end);

  while not Terminated do
  begin
    if Assigned(FCamera) then
    begin
      CaptureBitmap := TBitmap.Create;
      try
        // 获取摄像头当前帧
        FCamera.GetCurrentFrame(CaptureBitmap);
        if Assigned(FOnImageReady) then
        begin
          // 同步到主线程更新UI
          TThread.Synchronize(nil, procedure
          begin
            FOnImageReady(CaptureBitmap);
          end);
        end;
        Sleep(30); // 控制帧率
      finally
        CaptureBitmap.Free;
      end;
    end;
  end;
end;

procedure TCameraThread.StopCamera;
begin
  if Assigned(FCamera) then
  begin
    TThread.Synchronize(nil, procedure
    begin
      FCamera.StopCapture; // 先停止捕获
      FCamera.Destroy;
      FCamera := nil;
    end);
  end;
  Terminate;
  WaitFor;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(FCameraThread) then
  begin
    FCameraThread := TCameraThread.Create(Panel1, UpdateCameraImage);
    FCameraThread.Start;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if Assigned(FCameraThread) then
  begin
    FCameraThread.StopCamera;
    FCameraThread.Free;
    FCameraThread := nil;
  end;
end;

procedure TForm1.UpdateCameraImage(const ABitmap: TBitmap);
begin
  Image1.Picture.Bitmap.Assign(ABitmap);
end;

end.

关键修复点

  • 保存线程引用:用FCameraThread成员变量保存线程对象,方便后续停止和销毁。
  • 线程内安全停止摄像头:新增StopCamera方法,先调用摄像头的停止捕获方法,再销毁Camera,同时终止线程并等待线程结束。
  • 主线程操作VCL组件:所有涉及VCL组件(Panel、Image)的操作都通过TThread.Synchronize同步到主线程,避免跨线程访问风险。
  • 手动管理线程生命周期:设置FreeOnTerminate := False,确保线程在我们控制下销毁。

内容的提问来源于stack exchange,提问作者FLASHCODER

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:54:19