Showing
4 changed files
with
38 additions
and
29 deletions
| 1 | +## 4.0.1 | ||
| 2 | +Bugs fixed: | ||
| 3 | +* [iOS] Fixed a crash with a nil capture session when starting the camera. (thanks @navaronbracke !) | ||
| 4 | + | ||
| 1 | ## 4.0.0 | 5 | ## 4.0.0 |
| 2 | BREAKING CHANGES: | 6 | BREAKING CHANGES: |
| 3 | * [Android] compileSdk has been upgraded to version 34. | 7 | * [Android] compileSdk has been upgraded to version 34. |
| @@ -17,7 +17,7 @@ typealias ZoomScaleChangeCallback = ((Double?) -> ()) | @@ -17,7 +17,7 @@ typealias ZoomScaleChangeCallback = ((Double?) -> ()) | ||
| 17 | 17 | ||
| 18 | public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, FlutterTexture { | 18 | public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, FlutterTexture { |
| 19 | /// Capture session of the camera | 19 | /// Capture session of the camera |
| 20 | - var captureSession: AVCaptureSession! | 20 | + var captureSession: AVCaptureSession? |
| 21 | 21 | ||
| 22 | /// The selected camera | 22 | /// The selected camera |
| 23 | var device: AVCaptureDevice! | 23 | var device: AVCaptureDevice! |
| @@ -173,7 +173,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -173,7 +173,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 173 | /// Start scanning for barcodes | 173 | /// Start scanning for barcodes |
| 174 | func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { | 174 | func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { |
| 175 | self.detectionSpeed = detectionSpeed | 175 | self.detectionSpeed = detectionSpeed |
| 176 | - if (device != nil) { | 176 | + if (device != nil || captureSession != nil) { |
| 177 | throw MobileScannerError.alreadyStarted | 177 | throw MobileScannerError.alreadyStarted |
| 178 | } | 178 | } |
| 179 | 179 | ||
| @@ -216,17 +216,17 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -216,17 +216,17 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 216 | device.unlockForConfiguration() | 216 | device.unlockForConfiguration() |
| 217 | } catch {} | 217 | } catch {} |
| 218 | 218 | ||
| 219 | - captureSession.beginConfiguration() | 219 | + captureSession!.beginConfiguration() |
| 220 | 220 | ||
| 221 | // Add device input | 221 | // Add device input |
| 222 | do { | 222 | do { |
| 223 | let input = try AVCaptureDeviceInput(device: device) | 223 | let input = try AVCaptureDeviceInput(device: device) |
| 224 | - captureSession.addInput(input) | 224 | + captureSession!.addInput(input) |
| 225 | } catch { | 225 | } catch { |
| 226 | throw MobileScannerError.cameraError(error) | 226 | throw MobileScannerError.cameraError(error) |
| 227 | } | 227 | } |
| 228 | 228 | ||
| 229 | - captureSession.sessionPreset = AVCaptureSession.Preset.photo | 229 | + captureSession!.sessionPreset = AVCaptureSession.Preset.photo |
| 230 | // Add video output. | 230 | // Add video output. |
| 231 | let videoOutput = AVCaptureVideoDataOutput() | 231 | let videoOutput = AVCaptureVideoDataOutput() |
| 232 | 232 | ||
| @@ -237,17 +237,21 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -237,17 +237,21 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 237 | // calls captureOutput() | 237 | // calls captureOutput() |
| 238 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) | 238 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) |
| 239 | 239 | ||
| 240 | - captureSession.addOutput(videoOutput) | 240 | + captureSession!.addOutput(videoOutput) |
| 241 | for connection in videoOutput.connections { | 241 | for connection in videoOutput.connections { |
| 242 | connection.videoOrientation = .portrait | 242 | connection.videoOrientation = .portrait |
| 243 | if cameraPosition == .front && connection.isVideoMirroringSupported { | 243 | if cameraPosition == .front && connection.isVideoMirroringSupported { |
| 244 | connection.isVideoMirrored = true | 244 | connection.isVideoMirrored = true |
| 245 | } | 245 | } |
| 246 | } | 246 | } |
| 247 | - captureSession.commitConfiguration() | 247 | + captureSession!.commitConfiguration() |
| 248 | 248 | ||
| 249 | backgroundQueue.async { | 249 | backgroundQueue.async { |
| 250 | - self.captureSession.startRunning() | 250 | + guard let captureSession = self.captureSession else { |
| 251 | + return | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + captureSession.startRunning() | ||
| 251 | 255 | ||
| 252 | // After the capture session started, turn on the torch (if requested) | 256 | // After the capture session started, turn on the torch (if requested) |
| 253 | // and reset the zoom scale back to the default. | 257 | // and reset the zoom scale back to the default. |
| @@ -299,15 +303,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -299,15 +303,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 299 | 303 | ||
| 300 | /// Stop scanning for barcodes | 304 | /// Stop scanning for barcodes |
| 301 | func stop() throws { | 305 | func stop() throws { |
| 302 | - if (device == nil) { | 306 | + if (device == nil || captureSession == nil) { |
| 303 | throw MobileScannerError.alreadyStopped | 307 | throw MobileScannerError.alreadyStopped |
| 304 | } | 308 | } |
| 305 | - captureSession.stopRunning() | ||
| 306 | - for input in captureSession.inputs { | ||
| 307 | - captureSession.removeInput(input) | 309 | + |
| 310 | + captureSession!.stopRunning() | ||
| 311 | + for input in captureSession!.inputs { | ||
| 312 | + captureSession!.removeInput(input) | ||
| 308 | } | 313 | } |
| 309 | - for output in captureSession.outputs { | ||
| 310 | - captureSession.removeOutput(output) | 314 | + for output in captureSession!.outputs { |
| 315 | + captureSession!.removeOutput(output) | ||
| 311 | } | 316 | } |
| 312 | 317 | ||
| 313 | latestBuffer = nil | 318 | latestBuffer = nil |
| @@ -15,7 +15,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -15,7 +15,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 15 | var textureId: Int64! | 15 | var textureId: Int64! |
| 16 | 16 | ||
| 17 | // Capture session of the camera | 17 | // Capture session of the camera |
| 18 | - var captureSession: AVCaptureSession! | 18 | + var captureSession: AVCaptureSession? |
| 19 | 19 | ||
| 20 | // The selected camera | 20 | // The selected camera |
| 21 | weak var device: AVCaptureDevice! | 21 | weak var device: AVCaptureDevice! |
| @@ -239,7 +239,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -239,7 +239,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 241 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 242 | - if (device != nil) { | 242 | + if (device != nil || captureSession != nil) { |
| 243 | result(FlutterError(code: "MobileScanner", | 243 | result(FlutterError(code: "MobileScanner", |
| 244 | message: "Called start() while already started!", | 244 | message: "Called start() while already started!", |
| 245 | details: nil)) | 245 | details: nil)) |
| @@ -289,17 +289,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -289,17 +289,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil) | 291 | device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil) |
| 292 | - captureSession.beginConfiguration() | 292 | + captureSession!.beginConfiguration() |
| 293 | 293 | ||
| 294 | // Add device input | 294 | // Add device input |
| 295 | do { | 295 | do { |
| 296 | let input = try AVCaptureDeviceInput(device: device) | 296 | let input = try AVCaptureDeviceInput(device: device) |
| 297 | - captureSession.addInput(input) | 297 | + captureSession!.addInput(input) |
| 298 | } catch { | 298 | } catch { |
| 299 | result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) | 299 | result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) |
| 300 | return | 300 | return |
| 301 | } | 301 | } |
| 302 | - captureSession.sessionPreset = AVCaptureSession.Preset.photo | 302 | + captureSession!.sessionPreset = AVCaptureSession.Preset.photo |
| 303 | // Add video output. | 303 | // Add video output. |
| 304 | let videoOutput = AVCaptureVideoDataOutput() | 304 | let videoOutput = AVCaptureVideoDataOutput() |
| 305 | 305 | ||
| @@ -307,15 +307,15 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -307,15 +307,15 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 307 | videoOutput.alwaysDiscardsLateVideoFrames = true | 307 | videoOutput.alwaysDiscardsLateVideoFrames = true |
| 308 | 308 | ||
| 309 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) | 309 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) |
| 310 | - captureSession.addOutput(videoOutput) | 310 | + captureSession!.addOutput(videoOutput) |
| 311 | for connection in videoOutput.connections { | 311 | for connection in videoOutput.connections { |
| 312 | // connection.videoOrientation = .portrait | 312 | // connection.videoOrientation = .portrait |
| 313 | if position == .front && connection.isVideoMirroringSupported { | 313 | if position == .front && connection.isVideoMirroringSupported { |
| 314 | connection.isVideoMirrored = true | 314 | connection.isVideoMirrored = true |
| 315 | } | 315 | } |
| 316 | } | 316 | } |
| 317 | - captureSession.commitConfiguration() | ||
| 318 | - captureSession.startRunning() | 317 | + captureSession!.commitConfiguration() |
| 318 | + captureSession!.startRunning() | ||
| 319 | let dimensions = CMVideoFormatDescriptionGetDimensions(device.activeFormat.formatDescription) | 319 | let dimensions = CMVideoFormatDescriptionGetDimensions(device.activeFormat.formatDescription) |
| 320 | let size = ["width": Double(dimensions.width), "height": Double(dimensions.height)] | 320 | let size = ["width": Double(dimensions.width), "height": Double(dimensions.height)] |
| 321 | let answer: [String : Any?] = ["textureId": textureId, "size": size, "torchable": device.hasTorch] | 321 | let answer: [String : Any?] = ["textureId": textureId, "size": size, "torchable": device.hasTorch] |
| @@ -374,17 +374,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -374,17 +374,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 374 | // } | 374 | // } |
| 375 | 375 | ||
| 376 | func stop(_ result: FlutterResult) { | 376 | func stop(_ result: FlutterResult) { |
| 377 | - if (device == nil) { | 377 | + if (device == nil || captureSession == nil) { |
| 378 | result(nil) | 378 | result(nil) |
| 379 | 379 | ||
| 380 | return | 380 | return |
| 381 | } | 381 | } |
| 382 | - captureSession.stopRunning() | ||
| 383 | - for input in captureSession.inputs { | ||
| 384 | - captureSession.removeInput(input) | 382 | + captureSession!.stopRunning() |
| 383 | + for input in captureSession!.inputs { | ||
| 384 | + captureSession!.removeInput(input) | ||
| 385 | } | 385 | } |
| 386 | - for output in captureSession.outputs { | ||
| 387 | - captureSession.removeOutput(output) | 386 | + for output in captureSession!.outputs { |
| 387 | + captureSession!.removeOutput(output) | ||
| 388 | } | 388 | } |
| 389 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) | 389 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) |
| 390 | registry.unregisterTexture(textureId) | 390 | registry.unregisterTexture(textureId) |
| 1 | name: mobile_scanner | 1 | name: mobile_scanner |
| 2 | 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. | 2 | 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. |
| 3 | -version: 4.0.0 | 3 | +version: 4.0.1 |
| 4 | repository: https://github.com/juliansteenbakker/mobile_scanner | 4 | repository: https://github.com/juliansteenbakker/mobile_scanner |
| 5 | 5 | ||
| 6 | screenshots: | 6 | screenshots: |
-
Please register or login to post a comment