Better memory footprint and using a dispatch queue
By not passing in the buffer to the barcode detect and using an image instead the footprint of the memory usage is significantly reduced.
Showing
1 changed file
with
48 additions
and
31 deletions
| @@ -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! |
| @@ -95,7 +96,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -95,7 +96,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | var nextScanTime = 0.0 | 98 | var nextScanTime = 0.0 |
| 98 | - var imagesCurrentlyBeingProcessed = 0 | 99 | + var imagesCurrentlyBeingProcessed = false |
| 99 | 100 | ||
| 100 | // Gets called when a new image is added to the buffer | 101 | // Gets called when a new image is added to the buffer |
| 101 | public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { | 102 | public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { |
| @@ -111,45 +112,47 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -111,45 +112,47 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 111 | registry.textureFrameAvailable(textureId) | 112 | registry.textureFrameAvailable(textureId) |
| 112 | 113 | ||
| 113 | let currentTime = Date().timeIntervalSince1970 | 114 | let currentTime = Date().timeIntervalSince1970 |
| 114 | - let eligibleForScan = currentTime > nextScanTime && imagesCurrentlyBeingProcessed == 0; | 115 | + let eligibleForScan = currentTime > nextScanTime && imagesCurrentlyBeingProcessed == false |
| 115 | if ((detectionSpeed == DetectionSpeed.normal || detectionSpeed == DetectionSpeed.noDuplicates) && eligibleForScan || detectionSpeed == DetectionSpeed.unrestricted) { | 116 | if ((detectionSpeed == DetectionSpeed.normal || detectionSpeed == DetectionSpeed.noDuplicates) && eligibleForScan || detectionSpeed == DetectionSpeed.unrestricted) { |
| 116 | nextScanTime = currentTime + timeoutSeconds | 117 | nextScanTime = currentTime + timeoutSeconds |
| 117 | - imagesCurrentlyBeingProcessed += 1 | ||
| 118 | - let imageRequestHandler = VNImageRequestHandler( | ||
| 119 | - cvPixelBuffer: latestBuffer, | ||
| 120 | - orientation: .right) | ||
| 121 | - | 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!) | ||
| 122 | do { | 126 | do { |
| 123 | - let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [self] (request, error) in | ||
| 124 | - imagesCurrentlyBeingProcessed -= 1 | 127 | + let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in |
| 128 | + self?.imagesCurrentlyBeingProcessed = false | ||
| 125 | if error == nil { | 129 | if error == nil { |
| 126 | if let results = request.results as? [VNBarcodeObservation] { | 130 | if let results = request.results as? [VNBarcodeObservation] { |
| 127 | for barcode in results { | 131 | for barcode in results { |
| 128 | - if self.scanWindow != nil { | ||
| 129 | - let match = self.isbarCodeInScanWindow(self.scanWindow!, barcode, self.latestBuffer) | ||
| 130 | - if (!match) { | 132 | + if self?.scanWindow != nil && cgImage != nil { |
| 133 | + let match = self?.isbarCodeInScanWindow(self!.scanWindow!, barcode, cgImage!) | ||
| 134 | + if (match == false) { | ||
| 131 | continue | 135 | continue |
| 132 | } | 136 | } |
| 133 | } | 137 | } |
| 134 | 138 | ||
| 135 | - let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "") | ||
| 136 | - let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcode.symbology.toInt as Any?]] | ||
| 137 | - self.sink?(event) | ||
| 138 | - | ||
| 139 | - // if barcodeType == "QR" { | ||
| 140 | - // let image = CIImage(image: source) | ||
| 141 | - // image?.cropping(to: barcode.boundingBox) | ||
| 142 | - // self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!) | ||
| 143 | - // } | 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 | +// } | ||
| 144 | } | 147 | } |
| 145 | } | 148 | } |
| 146 | } else { | 149 | } else { |
| 147 | print(error!.localizedDescription) | 150 | print(error!.localizedDescription) |
| 148 | } | 151 | } |
| 149 | }) | 152 | }) |
| 150 | - if(symbologies.isEmpty == false){ | 153 | + if(self?.symbologies.isEmpty == false){ |
| 151 | // add the symbologies the user wishes to support | 154 | // add the symbologies the user wishes to support |
| 152 | - barcodeRequest.symbologies = symbologies | 155 | + barcodeRequest.symbologies = self!.symbologies |
| 153 | } | 156 | } |
| 154 | try imageRequestHandler.perform([barcodeRequest]) | 157 | try imageRequestHandler.perform([barcodeRequest]) |
| 155 | } catch { | 158 | } catch { |
| @@ -157,6 +160,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -157,6 +160,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 157 | } | 160 | } |
| 158 | } | 161 | } |
| 159 | } | 162 | } |
| 163 | + } | ||
| 160 | 164 | ||
| 161 | func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 165 | func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 162 | if #available(macOS 10.14, *) { | 166 | if #available(macOS 10.14, *) { |
| @@ -199,6 +203,20 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -199,6 +203,20 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 199 | scanWindow = CGRect(x: minX, y: minY, width: width, height: height) | 203 | scanWindow = CGRect(x: minX, y: minY, width: width, height: height) |
| 200 | } | 204 | } |
| 201 | 205 | ||
| 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 | + | ||
| 202 | func isbarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: VNBarcodeObservation, _ inputImage: CVImageBuffer) -> Bool { | 220 | func isbarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: VNBarcodeObservation, _ inputImage: CVImageBuffer) -> Bool { |
| 203 | let size = CVImageBufferGetEncodedSize(inputImage) | 221 | let size = CVImageBufferGetEncodedSize(inputImage) |
| 204 | 222 | ||
| @@ -227,11 +245,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -227,11 +245,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 227 | 245 | ||
| 228 | let argReader = MapArgumentReader(call.arguments as? [String: Any]) | 246 | let argReader = MapArgumentReader(call.arguments as? [String: Any]) |
| 229 | 247 | ||
| 230 | -// let ratio: Int = argReader.int(key: "ratio") | ||
| 231 | - let torch: Bool = argReader.bool(key: "torch") ?? false | ||
| 232 | - let facing: Int = argReader.int(key: "facing") ?? 1 | ||
| 233 | - let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0 | ||
| 234 | - 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 | ||
| 235 | symbologies = argReader.toSymbology() | 253 | symbologies = argReader.toSymbology() |
| 236 | 254 | ||
| 237 | timeoutSeconds = Double(timeoutMs) / 1000.0 | 255 | timeoutSeconds = Double(timeoutMs) / 1000.0 |
| @@ -285,7 +303,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -285,7 +303,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 285 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) | 303 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) |
| 286 | captureSession.addOutput(videoOutput) | 304 | captureSession.addOutput(videoOutput) |
| 287 | for connection in videoOutput.connections { | 305 | for connection in videoOutput.connections { |
| 288 | -// connection.videoOrientation = .portrait | 306 | + // connection.videoOrientation = .portrait |
| 289 | if position == .front && connection.isVideoMirroringSupported { | 307 | if position == .front && connection.isVideoMirroringSupported { |
| 290 | connection.isVideoMirrored = true | 308 | connection.isVideoMirrored = true |
| 291 | } | 309 | } |
| @@ -472,5 +490,4 @@ extension VNBarcodeSymbology { | @@ -472,5 +490,4 @@ extension VNBarcodeSymbology { | ||
| 472 | return -1; | 490 | return -1; |
| 473 | } | 491 | } |
| 474 | } | 492 | } |
| 475 | - | ||
| 476 | } | 493 | } |
-
Please register or login to post a comment