Showing
1 changed file
with
84 additions
and
65 deletions
| @@ -53,11 +53,11 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -53,11 +53,11 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 53 | case "start": | 53 | case "start": |
| 54 | start(call, result) | 54 | start(call, result) |
| 55 | case "torch": | 55 | case "torch": |
| 56 | - torchNative(call, result) | 56 | + switchTorch(call, result) |
| 57 | case "analyze": | 57 | case "analyze": |
| 58 | - analyzeNative(call, result) | 58 | + switchAnalyzeMode(call, result) |
| 59 | case "stop": | 59 | case "stop": |
| 60 | - stopNative(result) | 60 | + stop(result) |
| 61 | default: | 61 | default: |
| 62 | result(FlutterMethodNotImplemented) | 62 | result(FlutterMethodNotImplemented) |
| 63 | } | 63 | } |
| @@ -83,58 +83,59 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -83,58 +83,59 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 83 | return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer) | 83 | return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer) |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | -// public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { | ||
| 87 | -// | ||
| 88 | -// latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) | ||
| 89 | -// registry.textureFrameAvailable(textureId) | ||
| 90 | -// | ||
| 91 | -// switch analyzeMode { | ||
| 92 | -// case 1: // barcode | ||
| 93 | -// if analyzing { | ||
| 94 | -// break | ||
| 95 | -// } | ||
| 96 | -// analyzing = true | ||
| 97 | -// let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) | ||
| 98 | -// let image = VisionImage(image: buffer!.image) | ||
| 99 | -// image.orientation = imageOrientation( | ||
| 100 | -// deviceOrientation: UIDevice.current.orientation, | ||
| 101 | -// defaultOrientation: .portrait | ||
| 102 | -// ) | ||
| 103 | -// | ||
| 104 | -// let scanner = BarcodeScanner.barcodeScanner() | ||
| 105 | -// scanner.process(image) { [self] barcodes, error in | ||
| 106 | -// if error == nil && barcodes != nil { | ||
| 107 | -// for barcode in barcodes! { | ||
| 108 | -// let event: [String: Any?] = ["name": "barcode", "data": barcode.data] | ||
| 109 | -// sink?(event) | ||
| 110 | -// } | ||
| 111 | -// } | ||
| 112 | -// analyzing = false | ||
| 113 | -// } | ||
| 114 | -// default: // none | ||
| 115 | -// break | ||
| 116 | -// } | ||
| 117 | -// } | ||
| 118 | - | ||
| 119 | -// func imageOrientation( | ||
| 120 | -// deviceOrientation: UIDeviceOrientation, | ||
| 121 | -// defaultOrientation: UIDeviceOrientation | ||
| 122 | -// ) -> UIImage.Orientation { | ||
| 123 | -// switch deviceOrientation { | ||
| 124 | -// case .portrait: | ||
| 125 | -// return position == .front ? .leftMirrored : .right | ||
| 126 | -// case .landscapeLeft: | ||
| 127 | -// return position == .front ? .downMirrored : .up | ||
| 128 | -// case .portraitUpsideDown: | ||
| 129 | -// return position == .front ? .rightMirrored : .left | ||
| 130 | -// case .landscapeRight: | ||
| 131 | -// return position == .front ? .upMirrored : .down | ||
| 132 | -// case .faceDown, .faceUp, .unknown: | ||
| 133 | -// return .up | ||
| 134 | -// @unknown default: | ||
| 135 | -// return imageOrientation(deviceOrientation: defaultOrientation, defaultOrientation: .portrait) | ||
| 136 | -// } | ||
| 137 | -// } | 86 | + // Gets called when a new image is added to the buffer |
| 87 | + public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { | ||
| 88 | + | ||
| 89 | + latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) | ||
| 90 | + registry.textureFrameAvailable(textureId) | ||
| 91 | + | ||
| 92 | + switch analyzeMode { | ||
| 93 | + case 1: // barcode | ||
| 94 | + if analyzing { | ||
| 95 | + break | ||
| 96 | + } | ||
| 97 | + analyzing = true | ||
| 98 | + let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) | ||
| 99 | + let image = VisionImage(image: buffer!.image) | ||
| 100 | + image.orientation = imageOrientation( | ||
| 101 | + deviceOrientation: UIDevice.current.orientation, | ||
| 102 | + defaultOrientation: .portrait | ||
| 103 | + ) | ||
| 104 | + | ||
| 105 | + let scanner = BarcodeScanner.barcodeScanner() | ||
| 106 | + scanner.process(image) { [self] barcodes, error in | ||
| 107 | + if error == nil && barcodes != nil { | ||
| 108 | + for barcode in barcodes! { | ||
| 109 | + let event: [String: Any?] = ["name": "barcode", "data": barcode.data] | ||
| 110 | + sink?(event) | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + analyzing = false | ||
| 114 | + } | ||
| 115 | + default: // none | ||
| 116 | + break | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + func imageOrientation( | ||
| 121 | + deviceOrientation: UIDeviceOrientation, | ||
| 122 | + defaultOrientation: UIDeviceOrientation | ||
| 123 | + ) -> UIImage.Orientation { | ||
| 124 | + switch deviceOrientation { | ||
| 125 | + case .portrait: | ||
| 126 | + return position == .front ? .leftMirrored : .right | ||
| 127 | + case .landscapeLeft: | ||
| 128 | + return position == .front ? .downMirrored : .up | ||
| 129 | + case .portraitUpsideDown: | ||
| 130 | + return position == .front ? .rightMirrored : .left | ||
| 131 | + case .landscapeRight: | ||
| 132 | + return position == .front ? .upMirrored : .down | ||
| 133 | + case .faceDown, .faceUp, .unknown: | ||
| 134 | + return .up | ||
| 135 | + @unknown default: | ||
| 136 | + return imageOrientation(deviceOrientation: defaultOrientation, defaultOrientation: .portrait) | ||
| 137 | + } | ||
| 138 | + } | ||
| 138 | 139 | ||
| 139 | func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 140 | func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 140 | let status = AVCaptureDevice.authorizationStatus(for: .video) | 141 | let status = AVCaptureDevice.authorizationStatus(for: .video) |
| @@ -158,22 +159,35 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -158,22 +159,35 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 158 | 159 | ||
| 159 | let argReader = MapArgumentReader(call.arguments as? [String: Any]) | 160 | let argReader = MapArgumentReader(call.arguments as? [String: Any]) |
| 160 | 161 | ||
| 161 | - guard let ratio = argReader.int(key: "ratio"), | ||
| 162 | - let torch = argReader.int(key: "torch"), | ||
| 163 | - let facing = argReader.int(key: "facing") else { | ||
| 164 | - result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing a required argument", details: "Expecting targetWidth, targetHeight, formats, and optionally heartbeatTimeout")) | ||
| 165 | - return | ||
| 166 | - } | 162 | +// let ratio: Int = argReader.int(key: "ratio") |
| 163 | + let torch: Bool = argReader.bool(key: "torch") ?? false | ||
| 164 | + let facing: Int = argReader.int(key: "facing") ?? 1 | ||
| 167 | 165 | ||
| 166 | + // Set the camera to use | ||
| 168 | position = facing == 0 ? AVCaptureDevice.Position.front : .back | 167 | position = facing == 0 ? AVCaptureDevice.Position.front : .back |
| 168 | + | ||
| 169 | + // Open the camera device | ||
| 169 | if #available(iOS 10.0, *) { | 170 | if #available(iOS 10.0, *) { |
| 170 | device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first | 171 | device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first |
| 171 | } else { | 172 | } else { |
| 172 | device = AVCaptureDevice.devices(for: .video).filter({$0.position == position}).first | 173 | device = AVCaptureDevice.devices(for: .video).filter({$0.position == position}).first |
| 173 | } | 174 | } |
| 175 | + | ||
| 176 | + // Enable the torch if parameter is set and torch is available | ||
| 177 | + if (device.hasTorch && device.isTorchAvailable) { | ||
| 178 | + do { | ||
| 179 | + try device.lockForConfiguration() | ||
| 180 | + device.torchMode = torch ? .on : .off | ||
| 181 | + device.unlockForConfiguration() | ||
| 182 | + } catch { | ||
| 183 | + error.throwNative(result) | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + | ||
| 174 | device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil) | 187 | device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil) |
| 175 | captureSession.beginConfiguration() | 188 | captureSession.beginConfiguration() |
| 176 | - // Add device input. | 189 | + |
| 190 | + // Add device input | ||
| 177 | do { | 191 | do { |
| 178 | let input = try AVCaptureDeviceInput(device: device) | 192 | let input = try AVCaptureDeviceInput(device: device) |
| 179 | captureSession.addInput(input) | 193 | captureSession.addInput(input) |
| @@ -205,7 +219,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -205,7 +219,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 205 | result(answer) | 219 | result(answer) |
| 206 | } | 220 | } |
| 207 | 221 | ||
| 208 | - func torchNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 222 | + func switchTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 209 | do { | 223 | do { |
| 210 | try device.lockForConfiguration() | 224 | try device.lockForConfiguration() |
| 211 | device.torchMode = call.arguments as! Int == 1 ? .on : .off | 225 | device.torchMode = call.arguments as! Int == 1 ? .on : .off |
| @@ -216,12 +230,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -216,12 +230,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 216 | } | 230 | } |
| 217 | } | 231 | } |
| 218 | 232 | ||
| 219 | - func analyzeNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 233 | + func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 220 | analyzeMode = call.arguments as! Int | 234 | analyzeMode = call.arguments as! Int |
| 221 | result(nil) | 235 | result(nil) |
| 222 | } | 236 | } |
| 223 | 237 | ||
| 224 | - func stopNative(_ result: FlutterResult) { | 238 | + func stop(_ result: FlutterResult) { |
| 225 | captureSession.stopRunning() | 239 | captureSession.stopRunning() |
| 226 | for input in captureSession.inputs { | 240 | for input in captureSession.inputs { |
| 227 | captureSession.removeInput(input) | 241 | captureSession.removeInput(input) |
| @@ -241,6 +255,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -241,6 +255,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 241 | result(nil) | 255 | result(nil) |
| 242 | } | 256 | } |
| 243 | 257 | ||
| 258 | + // Observer for torch state | ||
| 244 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { | 259 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { |
| 245 | switch keyPath { | 260 | switch keyPath { |
| 246 | case "torchMode": | 261 | case "torchMode": |
| @@ -270,6 +285,10 @@ class MapArgumentReader { | @@ -270,6 +285,10 @@ class MapArgumentReader { | ||
| 270 | return (args?[key] as? NSNumber)?.intValue | 285 | return (args?[key] as? NSNumber)?.intValue |
| 271 | } | 286 | } |
| 272 | 287 | ||
| 288 | + func bool(key: String) -> Bool? { | ||
| 289 | + return (args?[key] as? NSNumber)?.boolValue | ||
| 290 | + } | ||
| 291 | + | ||
| 273 | func stringArray(key: String) -> [String]? { | 292 | func stringArray(key: String) -> [String]? { |
| 274 | return args?[key] as? [String] | 293 | return args?[key] as? [String] |
| 275 | } | 294 | } |
-
Please register or login to post a comment