Navaron Bracke
Committed by GitHub

Merge pull request #725 from ryanduffyne/mac_os_fixes

fix: Mac os fixes
@@ -384,6 +384,7 @@ class MobileScannerController { @@ -384,6 +384,7 @@ class MobileScannerController {
384 barcodes: [ 384 barcodes: [
385 Barcode( 385 Barcode(
386 rawValue: (data as Map)['payload'] as String?, 386 rawValue: (data as Map)['payload'] as String?,
  387 + format: toFormat(data['symbology'] as int),
387 ), 388 ),
388 ], 389 ],
389 ), 390 ),
@@ -2,6 +2,7 @@ import AVFoundation @@ -2,6 +2,7 @@ import AVFoundation
2 import FlutterMacOS 2 import FlutterMacOS
3 import Vision 3 import Vision
4 import AppKit 4 import AppKit
  5 +import VideoToolbox
5 6
6 public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate { 7 public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate {
7 8
@@ -17,7 +18,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -17,7 +18,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
17 var captureSession: AVCaptureSession! 18 var captureSession: AVCaptureSession!
18 19
19 // The selected camera 20 // The selected camera
20 - var device: AVCaptureDevice! 21 + weak var device: AVCaptureDevice!
21 22
22 // Image to be sent to the texture 23 // Image to be sent to the texture
23 var latestBuffer: CVImageBuffer! 24 var latestBuffer: CVImageBuffer!
@@ -29,6 +30,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -29,6 +30,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
29 30
30 var timeoutSeconds: Double = 0 31 var timeoutSeconds: Double = 0
31 32
  33 + var symbologies:[VNBarcodeSymbology] = []
  34 +
32 35
33 // var analyzeMode: Int = 0 36 // var analyzeMode: Int = 0
34 var analyzing: Bool = false 37 var analyzing: Bool = false
@@ -93,7 +96,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -93,7 +96,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
93 } 96 }
94 97
95 var nextScanTime = 0.0 98 var nextScanTime = 0.0
96 - var imagesCurrentlyBeingProcessed = 0 99 + var imagesCurrentlyBeingProcessed = false
97 100
98 // Gets called when a new image is added to the buffer 101 // Gets called when a new image is added to the buffer
99 public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 102 public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
@@ -109,44 +112,52 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -109,44 +112,52 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
109 registry.textureFrameAvailable(textureId) 112 registry.textureFrameAvailable(textureId)
110 113
111 let currentTime = Date().timeIntervalSince1970 114 let currentTime = Date().timeIntervalSince1970
112 - let eligibleForScan = currentTime > nextScanTime && imagesCurrentlyBeingProcessed == 0; 115 + let eligibleForScan = currentTime > nextScanTime && !imagesCurrentlyBeingProcessed
113 if ((detectionSpeed == DetectionSpeed.normal || detectionSpeed == DetectionSpeed.noDuplicates) && eligibleForScan || detectionSpeed == DetectionSpeed.unrestricted) { 116 if ((detectionSpeed == DetectionSpeed.normal || detectionSpeed == DetectionSpeed.noDuplicates) && eligibleForScan || detectionSpeed == DetectionSpeed.unrestricted) {
114 nextScanTime = currentTime + timeoutSeconds 117 nextScanTime = currentTime + timeoutSeconds
115 - imagesCurrentlyBeingProcessed += 1  
116 - let imageRequestHandler = VNImageRequestHandler(  
117 - cvPixelBuffer: latestBuffer,  
118 - orientation: .right)  
119 - 118 + imagesCurrentlyBeingProcessed = true
  119 + DispatchQueue.global(qos: .userInitiated).async { [weak self] in
  120 + if(self!.latestBuffer == nil){
  121 + return
  122 + }
  123 + var cgImage: CGImage?
  124 + VTCreateCGImageFromCVPixelBuffer(self!.latestBuffer, options: nil, imageOut: &cgImage)
  125 + let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage!)
120 do { 126 do {
121 - try imageRequestHandler.perform([VNDetectBarcodesRequest { [self] (request, error) in  
122 - imagesCurrentlyBeingProcessed -= 1 127 + let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in
  128 + self?.imagesCurrentlyBeingProcessed = false
123 if error == nil { 129 if error == nil {
124 if let results = request.results as? [VNBarcodeObservation] { 130 if let results = request.results as? [VNBarcodeObservation] {
125 for barcode in results { 131 for barcode in results {
126 - if self.scanWindow != nil {  
127 - let match = self.isbarCodeInScanWindow(self.scanWindow!, barcode, self.latestBuffer) 132 + if self?.scanWindow != nil && cgImage != nil {
  133 + let match = self?.isBarCodeInScanWindow(self!.scanWindow!, barcode, cgImage!) ?? false
128 if (!match) { 134 if (!match) {
129 continue 135 continue
130 } 136 }
131 } 137 }
132 138
133 - let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "")  
134 - let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]]  
135 - self.sink?(event)  
136 -  
137 - // if barcodeType == "QR" {  
138 - // let image = CIImage(image: source)  
139 - // image?.cropping(to: barcode.boundingBox)  
140 - // self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!)  
141 - // } 139 + DispatchQueue.main.async {
  140 + self?.sink?(["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcode.symbology.toInt as Any?]] as [String : Any])
  141 + }
  142 +// if barcodeType == "QR" {
  143 +// let image = CIImage(image: source)
  144 +// image?.cropping(to: barcode.boundingBox)
  145 +// self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!)
  146 +// }
142 } 147 }
143 } 148 }
144 } else { 149 } else {
145 - print(error!.localizedDescription) 150 + self?.sink?(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil))
  151 + }
  152 + })
  153 + if(self?.symbologies.isEmpty == false){
  154 + // add the symbologies the user wishes to support
  155 + barcodeRequest.symbologies = self!.symbologies
  156 + }
  157 + try imageRequestHandler.perform([barcodeRequest])
  158 + } catch let e {
  159 + self?.sink?(FlutterError(code: "MobileScanner", message: e.localizedDescription, details: nil))
146 } 160 }
147 - }])  
148 - } catch {  
149 - print(error)  
150 } 161 }
151 } 162 }
152 } 163 }
@@ -192,7 +203,21 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -192,7 +203,21 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
192 scanWindow = CGRect(x: minX, y: minY, width: width, height: height) 203 scanWindow = CGRect(x: minX, y: minY, width: width, height: height)
193 } 204 }
194 205
195 - func isbarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: VNBarcodeObservation, _ inputImage: CVImageBuffer) -> Bool { 206 + func isBarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: VNBarcodeObservation, _ inputImage: CGImage) -> Bool {
  207 +
  208 + let imageWidth = CGFloat(inputImage.width);
  209 + let imageHeight = CGFloat(inputImage.height);
  210 +
  211 + let minX = scanWindow.minX * imageWidth
  212 + let minY = scanWindow.minY * imageHeight
  213 + let width = scanWindow.width * imageWidth
  214 + let height = scanWindow.height * imageHeight
  215 +
  216 + let scaledScanWindow = CGRect(x: minX, y: minY, width: width, height: height)
  217 + return scaledScanWindow.contains(barcode.boundingBox)
  218 + }
  219 +
  220 + func isBarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: VNBarcodeObservation, _ inputImage: CVImageBuffer) -> Bool {
196 let size = CVImageBufferGetEncodedSize(inputImage) 221 let size = CVImageBufferGetEncodedSize(inputImage)
197 222
198 let imageWidth = size.width; 223 let imageWidth = size.width;
@@ -220,13 +245,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -220,13 +245,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
220 245
221 let argReader = MapArgumentReader(call.arguments as? [String: Any]) 246 let argReader = MapArgumentReader(call.arguments as? [String: Any])
222 247
223 -// let ratio: Int = argReader.int(key: "ratio")  
224 - let torch: Bool = argReader.bool(key: "torch") ?? false  
225 - let facing: Int = argReader.int(key: "facing") ?? 1  
226 - let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0  
227 - let timeoutMs: Int = (call.arguments as! Dictionary<String, Any?>)["timeout"] as? Int ?? 0 248 + // let ratio: Int = argReader.int(key: "ratio")
  249 + let torch:Bool = argReader.bool(key: "torch") ?? false
  250 + let facing:Int = argReader.int(key: "facing") ?? 1
  251 + let speed:Int = argReader.int(key: "speed") ?? 0
  252 + let timeoutMs:Int = argReader.int(key: "timeout") ?? 0
  253 + symbologies = argReader.toSymbology()
228 254
229 - timeoutSeconds = Double(timeoutMs) * 1000.0 255 + timeoutSeconds = Double(timeoutMs) / 1000.0
230 detectionSpeed = DetectionSpeed(rawValue: speed)! 256 detectionSpeed = DetectionSpeed(rawValue: speed)!
231 257
232 // Set the camera to use 258 // Set the camera to use
@@ -277,7 +303,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -277,7 +303,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
277 videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) 303 videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
278 captureSession.addOutput(videoOutput) 304 captureSession.addOutput(videoOutput)
279 for connection in videoOutput.connections { 305 for connection in videoOutput.connections {
280 -// connection.videoOrientation = .portrait 306 + // connection.videoOrientation = .portrait
281 if position == .front && connection.isVideoMirroringSupported { 307 if position == .front && connection.isVideoMirroringSupported {
282 connection.isVideoMirrored = true 308 connection.isVideoMirrored = true
283 } 309 }
@@ -373,8 +399,95 @@ class MapArgumentReader { @@ -373,8 +399,95 @@ class MapArgumentReader {
373 return args?[key] as? [String] 399 return args?[key] as? [String]
374 } 400 }
375 401
  402 + func toSymbology() -> [VNBarcodeSymbology] {
  403 + guard let syms:[Int] = args?["formats"] as? [Int] else {
  404 + return []
  405 + }
  406 + if(syms.contains(0)){
  407 + return []
  408 + }
  409 + var barcodeFormats:[VNBarcodeSymbology] = []
  410 + syms.forEach { id in
  411 + if let bc:VNBarcodeSymbology = VNBarcodeSymbology.fromInt(id) {
  412 + barcodeFormats.append(bc)
  413 + }
  414 + }
  415 + return barcodeFormats
  416 + }
  417 +
376 func floatArray(key: String) -> [CGFloat]? { 418 func floatArray(key: String) -> [CGFloat]? {
377 return args?[key] as? [CGFloat] 419 return args?[key] as? [CGFloat]
378 } 420 }
379 421
380 } 422 }
  423 +
  424 +extension VNBarcodeSymbology {
  425 +
  426 + static func fromInt(_ mapValue:Int) -> VNBarcodeSymbology? {
  427 + if #available(macOS 12.0, *) {
  428 + if(mapValue == 8){
  429 + return VNBarcodeSymbology.codabar
  430 + }
  431 + }
  432 + switch(mapValue){
  433 + case 1:
  434 + return VNBarcodeSymbology.code128
  435 + case 2:
  436 + return VNBarcodeSymbology.code39
  437 + case 4:
  438 + return VNBarcodeSymbology.code93
  439 + case 16:
  440 + return VNBarcodeSymbology.dataMatrix
  441 + case 32:
  442 + return VNBarcodeSymbology.ean13
  443 + case 64:
  444 + return VNBarcodeSymbology.ean8
  445 + case 128:
  446 + return VNBarcodeSymbology.itf14
  447 + case 256:
  448 + return VNBarcodeSymbology.qr
  449 + case 1024:
  450 + return VNBarcodeSymbology.upce
  451 + case 2048:
  452 + return VNBarcodeSymbology.pdf417
  453 + case 4096:
  454 + return VNBarcodeSymbology.aztec
  455 + default:
  456 + return nil
  457 + }
  458 + }
  459 +
  460 + var toInt:Int? {
  461 + if #available(macOS 12.0, *) {
  462 + if(self == VNBarcodeSymbology.codabar){
  463 + return 8
  464 + }
  465 + }
  466 + switch(self){
  467 + case VNBarcodeSymbology.code128:
  468 + return 1
  469 + case VNBarcodeSymbology.code39:
  470 + return 2
  471 + case VNBarcodeSymbology.code93:
  472 + return 4
  473 + case VNBarcodeSymbology.dataMatrix:
  474 + return 16
  475 + case VNBarcodeSymbology.ean13:
  476 + return 32
  477 + case VNBarcodeSymbology.ean8:
  478 + return 64
  479 + case VNBarcodeSymbology.itf14:
  480 + return 128
  481 + case VNBarcodeSymbology.qr:
  482 + return 256
  483 + case VNBarcodeSymbology.upce:
  484 + return 1024
  485 + case VNBarcodeSymbology.pdf417:
  486 + return 2048
  487 + case VNBarcodeSymbology.aztec:
  488 + return 4096
  489 + default:
  490 + return -1;
  491 + }
  492 + }
  493 +}