Committed by
GitHub
Merge pull request #725 from ryanduffyne/mac_os_fixes
fix: Mac os fixes
Showing
2 changed files
with
147 additions
and
33 deletions
| @@ -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 | +} |
-
Please register or login to post a comment