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

如何在Delphi 10.4 Firemonkey中获取流畅的相机多帧图像以实现高效二维码扫描?

Hey, I totally get where you're coming from—using TakePhoto for QR scanning is way too limiting, and the TCameraComponent's slow startup and stutter can be frustrating. Let's walk through some solid solutions to get that smooth, real-time scanning experience you're after, either by optimizing what you have or tapping into native APIs.

解决Delphi 10.4 Firemonkey中流畅二维码扫描的方案

一、先优化TCameraComponent的性能

Before jumping to native APIs, let's fix the existing component—most performance issues here are fixable with better configuration:

  • Pick a reasonable resolution: Skip ultra-high-res options; opt for a mid-tier resolution (like 720p) from SupportedResolutions. It balances scan accuracy and frame rate.
  • Throttle QR parsing: Don't parse every single frame in OnSampleBufferReady—add a timer (e.g., 150ms interval) to check frames periodically, or only parse when the user triggers a scan. This cuts down CPU load.
  • Enable hardware acceleration: Make sure your project has hardware acceleration turned on (check Project Options > Android/iOS settings). Firemonkey's camera rendering relies on this for smoothness.
  • Pre-initialize the component: Initialize TCameraComponent when your form loads, not right before scanning. Keep it hidden until needed—this eliminates the startup delay.

Example code snippet:

procedure TMainForm.FormCreate(Sender: TObject);
begin
  // Pre-initialize camera on form load
  if TPlatformServices.Current.SupportsPlatformService(IFMXCameraService) then
  begin
    CameraComponent1.Active := True;
    // Pick a mid-range resolution (adjust index based on your device's supported list)
    if CameraComponent1.SupportedResolutions.Count > 1 then
      CameraComponent1.Resolution := CameraComponent1.SupportedResolutions[1];
    CameraComponent1.Visible := False;
  end;
end;

procedure TMainForm.btnStartScanClick(Sender: TObject);
var
  ScanTimer: TTimer;
begin
  CameraComponent1.Visible := True;
  // Set up periodic QR parsing
  ScanTimer := TTimer.Create(nil);
  ScanTimer.Interval := 150;
  ScanTimer.OnTimer := ParseQrFrame;
  ScanTimer.Enabled := True;
end;

procedure TMainForm.ParseQrFrame(Sender: TObject);
var
  FrameBitmap: TBitmap;
  QrResult: string;
begin
  FrameBitmap := TBitmap.Create;
  try
    CameraComponent1.SampleBufferToBitmap(FrameBitmap, True);
    // Use your QR parsing library (e.g., ZXing) here
    // QrResult := ParseQRFromBitmap(FrameBitmap);
    // if QrResult <> '' then
    // begin
    //   ShowMessage('Scanned: ' + QrResult);
    //   (Sender as TTimer).Enabled := False;
    // end;
  finally
    FrameBitmap.Free;
  end;
end;

二、Call Android Native APIs for Smooth Scanning

If optimizing the component doesn't cut it, Android's native QR scanning APIs (like Google ML Kit or Mobile Vision) are way more performant:

  1. Configure your Android project: Add camera permissions to AndroidManifest.xml, and include the ML Kit dependency (e.g., com.google.mlkit:barcode-scanning:17.2.0).
  2. Bridge to Delphi via JNI: Call the native scanning activity or implement the scan logic directly in Java/Kotlin, then pass results back to Delphi.

Example JNI bridge code:

procedure TMainForm.LaunchAndroidQrScanner;
var
  ScanIntent: JIntent;
begin
  // Launch Google ML Kit's scan activity (adjust package name if using a custom implementation)
  ScanIntent := TJIntent.JavaClass.init(StringToJString('com.yourpackage.QrScannerActivity'));
  StartActivityForResult(ScanIntent, 1001);
end;

// Override to catch scan results
procedure TMainForm.ActivityResult(RequestCode, ResultCode: Integer; Data: JIntent);
var
  ScannedText: string;
begin
  inherited;
  if (RequestCode = 1001) and (ResultCode = TJActivity.JavaClass.RESULT_OK) then
  begin
    ScannedText := JStringToString(Data.getStringExtra(StringToJString('SCAN_RESULT')));
    ShowMessage('Scanned QR: ' + ScannedText);
  end;
end;

三、Use iOS Native AVFoundation for Flawless Scanning

iOS's AVFoundation framework has built-in, highly optimized QR scanning capabilities:

  1. Update Info.plist: Add the NSCameraUsageDescription key to explain why you need camera access.
  2. Tap into AVFoundation via Delphi's iOS units:

Example code snippet:

uses
  iOSapi.AVFoundation, iOSapi.CoreMedia, iOSapi.UIKit, Macapi.ObjectiveC;

var
  CaptureSession: AVCaptureSession;
  PreviewLayer: AVCaptureVideoPreviewLayer;

type
  TQrMetadataDelegate = class(TOCLocal, AVCaptureMetadataOutputObjectsDelegate)
  public
    procedure captureOutput(captureOutput: AVCaptureOutput; didOutputMetadataObjects: NSArray; fromConnection: AVCaptureConnection); cdecl;
  end;

procedure TMainForm.StartIOSQrScan;
var
  CameraDevice: AVCaptureDevice;
  DeviceInput: AVCaptureDeviceInput;
  MetadataOutput: AVCaptureMetadataOutput;
begin
  CaptureSession := TAVCaptureSession.Create;
  CameraDevice := TAVCaptureDevice.Wrap(TAVCaptureDevice.JavaClass.defaultDeviceWithMediaType(AVMediaTypeVideo));
  
  // Set up camera input
  DeviceInput := TAVCaptureDeviceInput.Wrap(TAVCaptureDeviceInput.JavaClass.deviceInputWithDevice(CameraDevice, nil));
  if CaptureSession.canAddInput(DeviceInput) then
    CaptureSession.addInput(DeviceInput);

  // Set up QR metadata output
  MetadataOutput := TAVCaptureMetadataOutput.Create;
  MetadataOutput.setMetadataObjectsDelegate(TQrMetadataDelegate.Create, nil);
  MetadataOutput.setMetadataObjectTypes(TNSArray.Wrap(TNSArray.JavaClass.arrayWithObjects(AVMetadataObjectTypeQRCode, nil)));
  if CaptureSession.canAddOutput(MetadataOutput) then
    CaptureSession.addOutput(MetadataOutput);

  // Add preview layer to your form
  PreviewLayer := TAVCaptureVideoPreviewLayer.Wrap(TAVCaptureVideoPreviewLayer.JavaClass.layerWithSession(CaptureSession));
  PreviewLayer.setFrame(ScanView.Bounds);
  ScanView.Layer.addSublayer(PreviewLayer);

  CaptureSession.startRunning;
end;

procedure TQrMetadataDelegate.captureOutput(captureOutput: AVCaptureOutput; didOutputMetadataObjects: NSArray; fromConnection: AVCaptureConnection);
var
  QrObject: AVMetadataMachineReadableCodeObject;
  ResultText: string;
begin
  if didOutputMetadataObjects.count > 0 then
  begin
    QrObject := TAVMetadataMachineReadableCodeObject.Wrap(didOutputMetadataObjects.objectAtIndex(0));
    ResultText := NSStrToStr(QrObject.stringValue);
    
    // Update UI on main thread
    TThread.Synchronize(nil, procedure
    begin
      ShowMessage('Scanned: ' + ResultText);
      CaptureSession.stopRunning;
      PreviewLayer.removeFromSuperlayer;
    end);
  end;
end;

四、Third-Party Libraries to Save Time

If you don't want to mess with native code, consider using a battle-tested Delphi library like ZXing Delphi—it's optimized for Firemonkey, handles camera framing automatically, and delivers smooth scanning out of the box.

Start with optimizing the TCameraComponent first—it's the quickest win. If that still doesn't meet your needs, native APIs or third-party libraries will give you the performance you're looking for.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 17:43:10