如何在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.
一、先优化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
TCameraComponentwhen 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:
- 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). - 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:
- Update Info.plist: Add the
NSCameraUsageDescriptionkey to explain why you need camera access. - 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

