| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | // | 
					
						
							|  |  |  | //  CameraSession+CodeScanner.swift | 
					
						
							|  |  |  | //  VisionCamera | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //  Created by Marc Rousavy on 11.10.23. | 
					
						
							|  |  |  | //  Copyright © 2023 mrousavy. All rights reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import AVFoundation | 
					
						
							|  |  |  | import Foundation | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension CameraSession: AVCaptureMetadataOutputObjectsDelegate { | 
					
						
							|  |  |  |   public func metadataOutput(_: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from _: AVCaptureConnection) { | 
					
						
							|  |  |  |     guard let onCodeScanned = delegate?.onCodeScanned else { | 
					
						
							|  |  |  |       // No delegate callback | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     guard !metadataObjects.isEmpty else { | 
					
						
							|  |  |  |       // No codes detected | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     guard let device = videoDeviceInput?.device else { | 
					
						
							|  |  |  |       // No cameraId set | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     let size = device.activeFormat.videoDimensions | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Map codes to JS values | 
					
						
							|  |  |  |     let codes = metadataObjects.map { object in | 
					
						
							|  |  |  |       var value: String? | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |       var corners: [CGPoint]? | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       if let code = object as? AVMetadataMachineReadableCodeObject { | 
					
						
							|  |  |  |         value = code.stringValue | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |         corners = code.corners.map { | 
					
						
							|  |  |  |           CGPoint(x: $0.x * Double(size.width), y: $0.y * Double(size.height)) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |       let x = object.bounds.origin.x * Double(size.width) | 
					
						
							|  |  |  |       let y = object.bounds.origin.y * Double(size.height) | 
					
						
							|  |  |  |       let w = object.bounds.width * Double(size.width) | 
					
						
							|  |  |  |       let h = object.bounds.height * Double(size.height) | 
					
						
							|  |  |  |       let frame = CGRect(x: x, y: y, width: w, height: h) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |       return Code(type: object.type, value: value, frame: frame, corners: corners) | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Call delegate (JS) event | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |     onCodeScanned(codes, CodeScannerFrame(width: size.width, height: size.height)) | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /**
 | 
					
						
							|  |  |  |    A scanned QR/Barcode. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   struct Code { | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      Type of the scanned Code | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     let type: AVMetadataObject.ObjectType | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      Decoded value of the code | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     let value: String? | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      Location of the code on-screen, relative to the video output layer | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     let frame: CGRect | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |       Location of the code on-screen, relative to the video output layer | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     let corners: [CGPoint]? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |      Converts this Code to a JS Object (Dictionary) | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     func toJSValue() -> [String: AnyHashable] { | 
					
						
							|  |  |  |       return [ | 
					
						
							|  |  |  |         "type": type.descriptor, | 
					
						
							|  |  |  |         "value": value, | 
					
						
							|  |  |  |         "frame": [ | 
					
						
							|  |  |  |           "x": frame.origin.x, | 
					
						
							|  |  |  |           "y": frame.origin.y, | 
					
						
							|  |  |  |           "width": frame.size.width, | 
					
						
							|  |  |  |           "height": frame.size.height, | 
					
						
							|  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |         "corners": corners?.map { [ | 
					
						
							|  |  |  |           "x": $0.x, | 
					
						
							|  |  |  |           "y": $0.y, | 
					
						
							|  |  |  |         ] } ?? [], | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   struct CodeScannerFrame { | 
					
						
							|  |  |  |     let width: Int32 | 
					
						
							|  |  |  |     let height: Int32 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     func toJSValue() -> [String: AnyHashable] { | 
					
						
							|  |  |  |       return [ | 
					
						
							|  |  |  |         "width": width, | 
					
						
							|  |  |  |         "height": height, | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       ] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |