From 4279cf342719ac62a715a78459acc82d1e81407a Mon Sep 17 00:00:00 2001 From: Navaron Bracke Date: Wed, 28 Feb 2024 10:45:23 +0100 Subject: [PATCH 1/2] fix nil capture session bug --- CHANGELOG.md | 4 +++ ios/Classes/MobileScanner.swift | 33 ++++++++++++++----------- macos/Classes/MobileScannerPlugin.swift | 28 ++++++++++----------- pubspec.yaml | 2 +- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7951733c9..40082b8e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.0.1 +Bugs fixed: +* [iOS] Fixed a crash with a nil capture session when starting the camera. (thanks @navaronbracke !) + ## 4.0.0 BREAKING CHANGES: * [Android] compileSdk has been upgraded to version 34. diff --git a/ios/Classes/MobileScanner.swift b/ios/Classes/MobileScanner.swift index 224f66812..69ea169a1 100644 --- a/ios/Classes/MobileScanner.swift +++ b/ios/Classes/MobileScanner.swift @@ -17,7 +17,7 @@ typealias ZoomScaleChangeCallback = ((Double?) -> ()) public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, FlutterTexture { /// Capture session of the camera - var captureSession: AVCaptureSession! + var captureSession: AVCaptureSession? /// The selected camera var device: AVCaptureDevice! @@ -173,7 +173,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega /// Start scanning for barcodes func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { self.detectionSpeed = detectionSpeed - if (device != nil) { + if (device != nil || captureSession != nil) { throw MobileScannerError.alreadyStarted } @@ -216,17 +216,17 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega device.unlockForConfiguration() } catch {} - captureSession.beginConfiguration() + captureSession!.beginConfiguration() // Add device input do { let input = try AVCaptureDeviceInput(device: device) - captureSession.addInput(input) + captureSession!.addInput(input) } catch { throw MobileScannerError.cameraError(error) } - captureSession.sessionPreset = AVCaptureSession.Preset.photo + captureSession!.sessionPreset = AVCaptureSession.Preset.photo // Add video output. let videoOutput = AVCaptureVideoDataOutput() @@ -237,17 +237,21 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega // calls captureOutput() videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) - captureSession.addOutput(videoOutput) + captureSession!.addOutput(videoOutput) for connection in videoOutput.connections { connection.videoOrientation = .portrait if cameraPosition == .front && connection.isVideoMirroringSupported { connection.isVideoMirrored = true } } - captureSession.commitConfiguration() + captureSession!.commitConfiguration() backgroundQueue.async { - self.captureSession.startRunning() + guard let captureSession = self.captureSession else { + return + } + + captureSession.startRunning() // After the capture session started, turn on the torch (if requested) // and reset the zoom scale back to the default. @@ -299,15 +303,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega /// Stop scanning for barcodes func stop() throws { - if (device == nil) { + if (device == nil || captureSession == nil) { throw MobileScannerError.alreadyStopped } - captureSession.stopRunning() - for input in captureSession.inputs { - captureSession.removeInput(input) + + captureSession!.stopRunning() + for input in captureSession!.inputs { + captureSession!.removeInput(input) } - for output in captureSession.outputs { - captureSession.removeOutput(output) + for output in captureSession!.outputs { + captureSession!.removeOutput(output) } latestBuffer = nil diff --git a/macos/Classes/MobileScannerPlugin.swift b/macos/Classes/MobileScannerPlugin.swift index 1cda60775..0b478be44 100644 --- a/macos/Classes/MobileScannerPlugin.swift +++ b/macos/Classes/MobileScannerPlugin.swift @@ -15,7 +15,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, var textureId: Int64! // Capture session of the camera - var captureSession: AVCaptureSession! + var captureSession: AVCaptureSession? // The selected camera weak var device: AVCaptureDevice! @@ -239,7 +239,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, } func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - if (device != nil) { + if (device != nil || captureSession != nil) { result(FlutterError(code: "MobileScanner", message: "Called start() while already started!", details: nil)) @@ -289,17 +289,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, } device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil) - captureSession.beginConfiguration() + captureSession!.beginConfiguration() // Add device input do { let input = try AVCaptureDeviceInput(device: device) - captureSession.addInput(input) + captureSession!.addInput(input) } catch { result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) return } - captureSession.sessionPreset = AVCaptureSession.Preset.photo + captureSession!.sessionPreset = AVCaptureSession.Preset.photo // Add video output. let videoOutput = AVCaptureVideoDataOutput() @@ -307,15 +307,15 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, videoOutput.alwaysDiscardsLateVideoFrames = true videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) - captureSession.addOutput(videoOutput) + captureSession!.addOutput(videoOutput) for connection in videoOutput.connections { // connection.videoOrientation = .portrait if position == .front && connection.isVideoMirroringSupported { connection.isVideoMirrored = true } } - captureSession.commitConfiguration() - captureSession.startRunning() + captureSession!.commitConfiguration() + captureSession!.startRunning() let dimensions = CMVideoFormatDescriptionGetDimensions(device.activeFormat.formatDescription) let size = ["width": Double(dimensions.width), "height": Double(dimensions.height)] let answer: [String : Any?] = ["textureId": textureId, "size": size, "torchable": device.hasTorch] @@ -374,17 +374,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, // } func stop(_ result: FlutterResult) { - if (device == nil) { + if (device == nil || captureSession == nil) { result(nil) return } - captureSession.stopRunning() - for input in captureSession.inputs { - captureSession.removeInput(input) + captureSession!.stopRunning() + for input in captureSession!.inputs { + captureSession!.removeInput(input) } - for output in captureSession.outputs { - captureSession.removeOutput(output) + for output in captureSession!.outputs { + captureSession!.removeOutput(output) } device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) registry.unregisterTexture(textureId) diff --git a/pubspec.yaml b/pubspec.yaml index cbd5348be..0afa3e9db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: mobile_scanner description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS. -version: 4.0.0 +version: 4.0.1 repository: https://github.com/juliansteenbakker/mobile_scanner screenshots: From a427f8d3cf095d232d8e034d4ffdb3a845ac8f95 Mon Sep 17 00:00:00 2001 From: Navaron Bracke Date: Wed, 28 Feb 2024 10:53:40 +0100 Subject: [PATCH 2/2] fix lint warning --- example/lib/barcode_list_scanner_controller.dart | 4 ++-- example/lib/barcode_scanner_controller.dart | 4 ++-- example/lib/barcode_scanner_zoom.dart | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/lib/barcode_list_scanner_controller.dart b/example/lib/barcode_list_scanner_controller.dart index e8c81ec5e..e1a4f39aa 100644 --- a/example/lib/barcode_list_scanner_controller.dart +++ b/example/lib/barcode_list_scanner_controller.dart @@ -154,7 +154,7 @@ class _BarcodeListScannerWithControllerState ); if (image != null) { if (await controller.analyzeImage(image.path)) { - if (!mounted) return; + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Barcode found!'), @@ -162,7 +162,7 @@ class _BarcodeListScannerWithControllerState ), ); } else { - if (!mounted) return; + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No barcode found!'), diff --git a/example/lib/barcode_scanner_controller.dart b/example/lib/barcode_scanner_controller.dart index 9e1221d21..8aac24bbf 100644 --- a/example/lib/barcode_scanner_controller.dart +++ b/example/lib/barcode_scanner_controller.dart @@ -170,7 +170,7 @@ class _BarcodeScannerWithControllerState ); if (image != null) { if (await controller.analyzeImage(image.path)) { - if (!mounted) return; + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Barcode found!'), @@ -178,7 +178,7 @@ class _BarcodeScannerWithControllerState ), ); } else { - if (!mounted) return; + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No barcode found!'), diff --git a/example/lib/barcode_scanner_zoom.dart b/example/lib/barcode_scanner_zoom.dart index b6986fec3..665968b2d 100644 --- a/example/lib/barcode_scanner_zoom.dart +++ b/example/lib/barcode_scanner_zoom.dart @@ -171,7 +171,7 @@ class _BarcodeScannerWithZoomState extends State ); if (image != null) { if (await controller.analyzeImage(image.path)) { - if (!mounted) return; + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Barcode found!'), @@ -179,7 +179,7 @@ class _BarcodeScannerWithZoomState extends State ), ); } else { - if (!mounted) return; + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No barcode found!'),