用于 Delphi 的TEdgeBrowser-捕获选项卡内容并转发它

vfh0ocws  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(303)

我正在一个浏览器/查看器 Delphi 应用程序中使用X1 M0 N1 X组件。在对最初的实现进行了简短的学习曲线之后,我几乎没有遇到什么问题。
我现在已经到了可以试验一些功能(比如屏幕捕捉)的地步。
要将屏幕捕获到文件,只需添加:

EdgeBrowser1.CapturePreview(ScrFileName);

其中,ScrFileName是文件的路径,即c:\pic\screenshot.png
我希望每秒捕获屏幕(并使用Indy组件(TIdHTTPServer)在内部(http)中继它)。
我可以这样做,但是效率很低。有人能推荐一个替代方案吗?

procedure TMainForm.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
Const
  EOL = #13;
var
  Strm: TMemoryStream;
begin
  if ARequestInfo.Document = '' then
  begin
    AResponseInfo.Redirect('/');
  end
  else if ARequestInfo.Document = '/' then
  begin
    AResponseInfo.ResponseNo := 200;
    AResponseInfo.ContentType := 'text/html';
    AResponseInfo.ContentText := '<!DOCTYPE html>'+EOL+
      '<html>'+EOL+
      '<head>'+EOL+
      '<meta name="viewport" content="width=device-width, initial-scale=5">'+EOL+
      '<meta http-equiv="Refresh" content=1>'+EOL+
      '<style type="text/css">'+EOL+
      '  img.fullscr {'+EOL+
      '    display:block;'+EOL+
      '    width:100%;'+EOL+
      '  }'+EOL+
      '</style>'+EOL+
      '</head>'+EOL+
      '<body>'+EOL+
      '<img src="/image" class="fullscr">'+EOL+
      '</body>'+EOL+
      '</html>'+EOL;
  end
  else if ARequestInfo.Document = '/image' then
  begin
    Strm := TMemoryStream.Create;
    try
      Strm.Clear;
      Strm.LoadFromFile(ScrFileName);
      Strm.Position := 0;
      AResponseInfo.ResponseNo := 200;
      AResponseInfo.ContentType := 'image/png';
      AResponseInfo.ContentStream := Strm;
    except
      Strm.Free;
      raise;
    end;
  end
  else begin
    AResponseInfo.ResponseNo := 404;
  end;
end;
cigdeys3

cigdeys31#

您是否考虑过根本不将捕获的图像保存到磁盘?TEdgeBrowser.CapturePreview()有一个保存到TStream的重载。您可以定期将捕获的图像保存到TMemoryStream,并与TIdHTTPServer共享,以便将最新捕获的图像发送到HTTP客户端。
TIdHTTPServer是一个多线程组件,它的OnCommand...事件是在工作线程的上下文中触发的,因此请确保使用线程安全锁(如TCriticalSection)保护TMemoryStream
例如:

private
  // using 2 TMemoryStreams so Edge can be preparing a new capture
  // while TIdHTTPServer continues serving the previous capture to
  // clients until the new capture is ready...
  CapturingImageStream: TMemoryStream;
  ReadyImageStream: TMemoryStream;
  ImageLock: TCriticalSection;

  // TODO: keep track of last capture time and etag, in case clients
  // want to use Conditional-GET requests ('If-Modified-Since' and
  // 'If-None-Match' HTTP headers) to cache retrievals in case captures
  // haven't changed yet in between multiple GET requests...

...

procedure TMainForm.FormCreate(Sender: TObject);
begin
  CapturingImageStream := TMemoryStream.Create;
  ReadyImageStream := TMemoryStream.Create;
  ImageLock := TCriticalSection.Create;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  IdHTTPServer1.Active := False;
  CapturingImageStream.Free;
  ReadyImageStream.Free;
  ImageLock.Free;
end;

procedure TMainForm.Timer1Timer(Sender: TObject);
var
  Strm: TMemoryStream;
begin
  CapturingImageStream.Clear;
  EdgeBrowser1.CapturePreview(CapturingImageStream);
  ImageLock.Enter;
  try
    Strm := ReadyImageStream;
    ReadyImageStream := CapturingImageStream;
    CapturingImageStream := Strm;
  finally
    ImageLock.Leave;
  end;
end;

procedure TMainForm.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  if ARequestInfo.Document = '' then
  begin
    AResponseInfo.Redirect('/');
  end
  else if ARequestInfo.Document = '/' then
  begin
    AResponseInfo.ResponseNo := 200;
    AResponseInfo.ContentType := 'text/html';
    AResponseInfo.ContentText := '<!DOCTYPE html>'+sLineBreak+
      '<html>'+sLineBreak+
      '<head>'+sLineBreak+
      '<meta name="viewport" content="width=device-width, initial-scale=5">'+sLineBreak+
      '<meta http-equiv="Refresh" content=1>'+sLineBreak+
      '<style type="text/css">'+sLineBreak+
      '  img.fullscr {'+sLineBreak+
      '    display:block;'+sLineBreak+
      '    width:100%;'+sLineBreak+
      '  }'+sLineBreak+
      '</style>'+sLineBreak+
      '</head>'+sLineBreak+
      '<body>'+sLineBreak+
      '<img src="/image" class="fullscr">'+sLineBreak+
      '</body>'+sLineBreak+
      '</html>'+sLineBreak;
  end
  else if ARequestInfo.Document = '/image' then
  begin
    ImageLock.Enter;
    try
      // TODO: check request's 'If-Modified-Since' and 'If-None-Match'
      // headers, send 304 if the current capture hasn't changed yet...

      ReadyImageStream.Position := 0;
      AResponseInfo.ResponseNo := 200;
      AResponseInfo.ContentType := 'image/png';
      AResponseInfo.ContentStream := ReadyImageStream;
      AResponseInfo.FreeContentStream := False;
      // need to send the ContentStream inside the read lock...
      AResponseInfo.WriteHeader;
      AResponseInfo.WriteContent;
    finally
      ImageLock.Leave;
    end;
  end
  else begin
    AResponseInfo.ResponseNo := 404;
  end;
end;

为了进一步优化这一点,您可以考虑更改HTML以使用客户端 AJAX 或WebSocket脚本来定期请求和显示最新的图像,而不必每次都重新加载整个HTML页面。
例如,如果您要使用WebSocket,您可以在代码中添加WebSocket服务器(Indy目前还没有,但有一些第三方实现),并让WebSocket服务器在准备就绪时向浏览器推送一个新的捕获,而不必等待HTTP请求首先到达。

相关问题