| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  | // | 
					
						
							|  |  |  | //  CameraView.swift | 
					
						
							| 
									
										
										
										
											2021-06-21 22:42:46 +02:00
										 |  |  | //  mrousavy | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  | // | 
					
						
							|  |  |  | //  Created by Marc Rousavy on 09.11.20. | 
					
						
							| 
									
										
										
										
											2021-06-01 13:07:57 +02:00
										 |  |  | //  Copyright © 2020 mrousavy. All rights reserved. | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import AVFoundation | 
					
						
							|  |  |  | import Foundation | 
					
						
							|  |  |  | import UIKit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODOs for the CameraView which are currently too hard to implement either because of AVFoundation's limitations, or my brain capacity | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // CameraView+RecordVideo | 
					
						
							|  |  |  | // TODO: Better startRecording()/stopRecording() (promise + callback, wait for TurboModules/JSI) | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  | // CameraView+TakePhoto | 
					
						
							|  |  |  | // TODO: Photo HDR | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-09 10:53:29 +01:00
										 |  |  | // MARK: - CameraView | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | public final class CameraView: UIView, CameraSessionDelegate { | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  |   // pragma MARK: React Properties | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  |   // props that require reconfiguring | 
					
						
							|  |  |  |   @objc var cameraId: NSString? | 
					
						
							|  |  |  |   @objc var enableDepthData = false | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |   @objc var enableHighQualityPhotos = false | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  |   @objc var enablePortraitEffectsMatteDelivery = false | 
					
						
							| 
									
										
										
										
											2023-09-21 17:18:54 +02:00
										 |  |  |   @objc var enableBufferCompression = false | 
					
						
							| 
									
										
										
										
											2021-06-07 13:08:40 +02:00
										 |  |  |   // use cases | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |   @objc var photo = false | 
					
						
							|  |  |  |   @objc var video = false | 
					
						
							|  |  |  |   @objc var audio = false | 
					
						
							| 
									
										
										
										
											2021-07-12 15:16:03 +02:00
										 |  |  |   @objc var enableFrameProcessor = false | 
					
						
							| 
									
										
										
										
											2023-10-04 12:53:52 +02:00
										 |  |  |   @objc var codeScannerOptions: NSDictionary? | 
					
						
							| 
									
										
										
										
											2023-08-21 12:50:14 +02:00
										 |  |  |   @objc var pixelFormat: NSString? | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  |   // props that require format reconfiguring | 
					
						
							|  |  |  |   @objc var format: NSDictionary? | 
					
						
							|  |  |  |   @objc var fps: NSNumber? | 
					
						
							| 
									
										
										
										
											2023-11-15 18:33:12 +01:00
										 |  |  |   @objc var videoHdr = false | 
					
						
							|  |  |  |   @objc var photoHdr = false | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |   @objc var lowLightBoost = false | 
					
						
							| 
									
										
										
										
											2022-01-04 16:57:40 +01:00
										 |  |  |   @objc var orientation: NSString? | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  |   // other props | 
					
						
							|  |  |  |   @objc var isActive = false | 
					
						
							|  |  |  |   @objc var torch = "off" | 
					
						
							| 
									
										
										
										
											2021-07-29 11:44:22 +02:00
										 |  |  |   @objc var zoom: NSNumber = 1.0 // in "factor" | 
					
						
							| 
									
										
										
										
											2023-11-19 15:26:43 +01:00
										 |  |  |   @objc var exposure: NSNumber = 1.0 | 
					
						
							| 
									
										
										
										
											2023-02-21 15:00:48 +01:00
										 |  |  |   @objc var enableFpsGraph = false | 
					
						
							| 
									
										
										
										
											2021-06-03 15:42:02 +02:00
										 |  |  |   @objc var videoStabilizationMode: NSString? | 
					
						
							| 
									
										
										
										
											2023-09-23 10:14:27 +02:00
										 |  |  |   @objc var resizeMode: NSString = "cover" { | 
					
						
							|  |  |  |     didSet { | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       let parsed = try? ResizeMode(jsValue: resizeMode as String) | 
					
						
							|  |  |  |       previewView.resizeMode = parsed ?? .cover | 
					
						
							| 
									
										
										
										
											2023-09-23 10:14:27 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  |   // events | 
					
						
							|  |  |  |   @objc var onInitialized: RCTDirectEventBlock? | 
					
						
							|  |  |  |   @objc var onError: RCTDirectEventBlock? | 
					
						
							| 
									
										
										
										
											2023-12-09 21:09:55 +03:00
										 |  |  |   @objc var onStarted: RCTDirectEventBlock? | 
					
						
							|  |  |  |   @objc var onStopped: RCTDirectEventBlock? | 
					
						
							| 
									
										
										
										
											2021-10-11 18:27:23 +02:00
										 |  |  |   @objc var onViewReady: RCTDirectEventBlock? | 
					
						
							| 
									
										
										
										
											2024-07-15 09:55:47 +01:00
										 |  |  |   @objc var onInitReady: RCTDirectEventBlock? | 
					
						
							| 
									
										
										
										
											2024-07-15 09:50:39 +01:00
										 |  |  |   @objc var onVideoChunkReady: RCTDirectEventBlock? | 
					
						
							| 
									
										
										
										
											2023-10-04 12:53:52 +02:00
										 |  |  |   @objc var onCodeScanned: RCTDirectEventBlock? | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  |   // zoom | 
					
						
							|  |  |  |   @objc var enableZoomGesture = false { | 
					
						
							|  |  |  |     didSet { | 
					
						
							|  |  |  |       if enableZoomGesture { | 
					
						
							|  |  |  |         addPinchGestureRecognizer() | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         removePinchGestureRecognizer() | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // pragma MARK: Internal Properties | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |   var cameraSession: CameraSession | 
					
						
							| 
									
										
										
										
											2023-09-01 12:58:32 +02:00
										 |  |  |   var isMounted = false | 
					
						
							|  |  |  |   var isReady = false | 
					
						
							| 
									
										
										
										
											2023-07-20 15:30:04 +02:00
										 |  |  |   #if VISION_CAMERA_ENABLE_FRAME_PROCESSORS | 
					
						
							|  |  |  |     @objc public var frameProcessor: FrameProcessor? | 
					
						
							|  |  |  |   #endif | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  |   // CameraView+Zoom | 
					
						
							| 
									
										
										
										
											2023-09-01 12:58:32 +02:00
										 |  |  |   var pinchGestureRecognizer: UIPinchGestureRecognizer? | 
					
						
							|  |  |  |   var pinchScaleOffset: CGFloat = 1.0 | 
					
						
							| 
									
										
										
										
											2024-01-08 11:41:57 +01:00
										 |  |  |   private var currentConfigureCall: DispatchTime? | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-01 13:08:33 +02:00
										 |  |  |   var previewView: PreviewView | 
					
						
							| 
									
										
										
										
											2023-02-21 15:00:48 +01:00
										 |  |  |   #if DEBUG | 
					
						
							| 
									
										
										
										
											2023-09-01 12:58:32 +02:00
										 |  |  |     var fpsGraph: RCTFPSGraph? | 
					
						
							| 
									
										
										
										
											2023-02-21 15:00:48 +01:00
										 |  |  |   #endif | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  |   // pragma MARK: Setup | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  |   override public init(frame: CGRect) { | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     // Create CameraSession | 
					
						
							|  |  |  |     cameraSession = CameraSession() | 
					
						
							|  |  |  |     previewView = cameraSession.createPreviewView(frame: frame) | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  |     super.init(frame: frame) | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     cameraSession.delegate = self | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-01 15:07:16 +02:00
										 |  |  |     addSubview(previewView) | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 14:11:55 +02:00
										 |  |  |   @available(*, unavailable) | 
					
						
							|  |  |  |   required init?(coder _: NSCoder) { | 
					
						
							|  |  |  |     fatalError("init(coder:) is not implemented.") | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-11 18:27:23 +02:00
										 |  |  |   override public func willMove(toSuperview newSuperview: UIView?) { | 
					
						
							|  |  |  |     super.willMove(toSuperview: newSuperview) | 
					
						
							| 
									
										
										
										
											2023-02-21 15:00:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if newSuperview != nil { | 
					
						
							|  |  |  |       if !isMounted { | 
					
						
							|  |  |  |         isMounted = true | 
					
						
							| 
									
										
										
										
											2023-07-20 15:30:04 +02:00
										 |  |  |         onViewReady?(nil) | 
					
						
							| 
									
										
										
										
											2021-10-11 18:27:23 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-21 15:00:48 +01:00
										 |  |  |   override public func layoutSubviews() { | 
					
						
							| 
									
										
										
										
											2023-09-01 13:08:33 +02:00
										 |  |  |     previewView.frame = frame | 
					
						
							|  |  |  |     previewView.bounds = bounds | 
					
						
							| 
									
										
										
										
											2023-02-21 15:00:48 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |   func getPixelFormat() -> PixelFormat { | 
					
						
							|  |  |  |     // TODO: Use ObjC RCT enum parser for this | 
					
						
							|  |  |  |     if let pixelFormat = pixelFormat as? String { | 
					
						
							|  |  |  |       do { | 
					
						
							|  |  |  |         return try PixelFormat(jsValue: pixelFormat) | 
					
						
							|  |  |  |       } catch { | 
					
						
							|  |  |  |         if let error = error as? CameraError { | 
					
						
							|  |  |  |           onError(error) | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           onError(.unknown(message: error.localizedDescription, cause: error as NSError)) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return .native | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   func getTorch() -> Torch { | 
					
						
							|  |  |  |     // TODO: Use ObjC RCT enum parser for this | 
					
						
							|  |  |  |     if let torch = try? Torch(jsValue: torch) { | 
					
						
							|  |  |  |       return torch | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return .off | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  |   // pragma MARK: Props updating | 
					
						
							| 
									
										
										
										
											2021-03-31 15:43:29 +02:00
										 |  |  |   override public final func didSetProps(_ changedProps: [String]!) { | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     ReactLogger.log(level: .info, message: "Updating \(changedProps.count) props: [\(changedProps.joined(separator: ", "))]") | 
					
						
							| 
									
										
										
										
											2024-01-08 11:41:57 +01:00
										 |  |  |     let now = DispatchTime.now() | 
					
						
							|  |  |  |     currentConfigureCall = now | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cameraSession.configure { [self] config in | 
					
						
							|  |  |  |       // Check if we're still the latest call to configure { ... } | 
					
						
							|  |  |  |       guard currentConfigureCall == now else { | 
					
						
							|  |  |  |         // configure waits for a lock, and if a new call to update() happens in the meantime we can drop this one. | 
					
						
							|  |  |  |         // this works similar to how React implemented concurrent rendering, the newer call to update() has higher priority. | 
					
						
							|  |  |  |         ReactLogger.log(level: .info, message: "A new configure { ... } call arrived, aborting this one...") | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Input Camera Device | 
					
						
							|  |  |  |       config.cameraId = cameraId as? String | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Photo | 
					
						
							|  |  |  |       if photo { | 
					
						
							|  |  |  |         config.photo = .enabled(config: CameraConfiguration.Photo(enableHighQualityPhotos: enableHighQualityPhotos, | 
					
						
							|  |  |  |                                                                   enableDepthData: enableDepthData, | 
					
						
							|  |  |  |                                                                   enablePortraitEffectsMatte: enablePortraitEffectsMatteDelivery)) | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         config.photo = .disabled | 
					
						
							| 
									
										
										
										
											2023-02-21 15:00:48 +01:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-06-03 15:42:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Video/Frame Processor | 
					
						
							|  |  |  |       if video || enableFrameProcessor { | 
					
						
							|  |  |  |         config.video = .enabled(config: CameraConfiguration.Video(pixelFormat: getPixelFormat(), | 
					
						
							|  |  |  |                                                                   enableBufferCompression: enableBufferCompression, | 
					
						
							| 
									
										
										
										
											2023-11-15 18:33:12 +01:00
										 |  |  |                                                                   enableHdr: videoHdr, | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |                                                                   enableFrameProcessor: enableFrameProcessor)) | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         config.video = .disabled | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Audio | 
					
						
							|  |  |  |       if audio { | 
					
						
							|  |  |  |         config.audio = .enabled(config: CameraConfiguration.Audio()) | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         config.audio = .disabled | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Code Scanner | 
					
						
							|  |  |  |       if let codeScannerOptions { | 
					
						
							| 
									
										
										
										
											2023-10-16 16:56:39 +02:00
										 |  |  |         let options = try CodeScannerOptions(fromJsValue: codeScannerOptions) | 
					
						
							|  |  |  |         config.codeScanner = .enabled(config: CameraConfiguration.CodeScanner(options: options)) | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         config.codeScanner = .disabled | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-15 17:00:41 +01:00
										 |  |  |       // Video Stabilization | 
					
						
							|  |  |  |       if let jsVideoStabilizationMode = videoStabilizationMode as? String { | 
					
						
							|  |  |  |         let videoStabilizationMode = try VideoStabilizationMode(jsValue: jsVideoStabilizationMode) | 
					
						
							|  |  |  |         config.videoStabilizationMode = videoStabilizationMode | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         config.videoStabilizationMode = .off | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Orientation | 
					
						
							|  |  |  |       if let jsOrientation = orientation as? String { | 
					
						
							|  |  |  |         let orientation = try Orientation(jsValue: jsOrientation) | 
					
						
							|  |  |  |         config.orientation = orientation | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         config.orientation = .portrait | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-01-04 16:57:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Format | 
					
						
							|  |  |  |       if let jsFormat = format { | 
					
						
							|  |  |  |         let format = try CameraDeviceFormat(jsValue: jsFormat) | 
					
						
							|  |  |  |         config.format = format | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         config.format = nil | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-06-07 13:08:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // Side-Props | 
					
						
							|  |  |  |       config.fps = fps?.int32Value | 
					
						
							|  |  |  |       config.enableLowLightBoost = lowLightBoost | 
					
						
							| 
									
										
										
										
											2023-11-23 22:19:44 +01:00
										 |  |  |       config.torch = try Torch(jsValue: torch) | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Zoom | 
					
						
							|  |  |  |       config.zoom = zoom.doubleValue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-19 15:26:43 +01:00
										 |  |  |       // Exposure | 
					
						
							|  |  |  |       config.exposure = exposure.floatValue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       // isActive | 
					
						
							|  |  |  |       config.isActive = isActive | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Store `zoom` offset for native pinch-gesture | 
					
						
							|  |  |  |     if changedProps.contains("zoom") { | 
					
						
							|  |  |  |       pinchScaleOffset = zoom.doubleValue | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set up Debug FPS Graph | 
					
						
							|  |  |  |     if changedProps.contains("enableFpsGraph") { | 
					
						
							|  |  |  |       DispatchQueue.main.async { | 
					
						
							|  |  |  |         self.setupFpsGraph() | 
					
						
							| 
									
										
										
										
											2021-06-07 13:08:40 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-01-19 12:35:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Prevent phone from going to sleep | 
					
						
							|  |  |  |     UIApplication.shared.isIdleTimerDisabled = isActive | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-09-01 15:07:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-01 13:08:33 +02:00
										 |  |  |   func setupFpsGraph() { | 
					
						
							|  |  |  |     #if DEBUG | 
					
						
							|  |  |  |       if enableFpsGraph { | 
					
						
							|  |  |  |         if fpsGraph != nil { return } | 
					
						
							|  |  |  |         fpsGraph = RCTFPSGraph(frame: CGRect(x: 10, y: 54, width: 75, height: 45), color: .red) | 
					
						
							|  |  |  |         fpsGraph!.layer.zPosition = 9999.0 | 
					
						
							|  |  |  |         addSubview(fpsGraph!) | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         fpsGraph?.removeFromSuperview() | 
					
						
							|  |  |  |         fpsGraph = nil | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     #endif | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-03-26 16:28:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-09 10:53:29 +01:00
										 |  |  |   // pragma MARK: Event Invokers | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   func onError(_ error: CameraError) { | 
					
						
							| 
									
										
										
										
											2021-03-29 14:12:04 +02:00
										 |  |  |     ReactLogger.log(level: .error, message: "Invoking onError(): \(error.message)") | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     guard let onError = onError else { | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-02-23 10:27:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-09 10:53:29 +01:00
										 |  |  |     var causeDictionary: [String: Any]? | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     if case let .unknown(_, cause) = error, | 
					
						
							|  |  |  |        let cause = cause { | 
					
						
							| 
									
										
										
										
											2021-03-09 10:53:29 +01:00
										 |  |  |       causeDictionary = [ | 
					
						
							|  |  |  |         "code": cause.code, | 
					
						
							|  |  |  |         "domain": cause.domain, | 
					
						
							| 
									
										
										
										
											2021-03-26 15:54:27 +01:00
										 |  |  |         "message": cause.description, | 
					
						
							| 
									
										
										
										
											2021-03-09 10:53:29 +01:00
										 |  |  |         "details": cause.userInfo, | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     onError([ | 
					
						
							|  |  |  |       "code": error.code, | 
					
						
							|  |  |  |       "message": error.message, | 
					
						
							|  |  |  |       "cause": causeDictionary ?? NSNull(), | 
					
						
							|  |  |  |     ]) | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-02-23 10:27:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |   func onSessionInitialized() { | 
					
						
							| 
									
										
										
										
											2021-03-29 14:12:04 +02:00
										 |  |  |     ReactLogger.log(level: .info, message: "Camera initialized!") | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     guard let onInitialized = onInitialized else { | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-09 21:09:55 +03:00
										 |  |  |     onInitialized([:]) | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-08-02 14:39:23 +01:00
										 |  |  |    | 
					
						
							|  |  |  |   func onCameraConfigurationChanged(_ configuration: CameraConfiguration?, _ difference: CameraConfiguration.Difference?) { | 
					
						
							|  |  |  |     guard let configuration, let difference else { return } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     if difference.orientationChanged, let connection = previewView.videoPreviewLayer.connection { | 
					
						
							|  |  |  |       let videoPreviewLayer = previewView.videoPreviewLayer | 
					
						
							|  |  |  |       connection.setOrientation(configuration.orientation) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-12-09 21:09:55 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   func onCameraStarted() { | 
					
						
							|  |  |  |     ReactLogger.log(level: .info, message: "Camera started!") | 
					
						
							|  |  |  |     guard let onStarted = onStarted else { | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     onStarted([:]) | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   func onCameraStopped() { | 
					
						
							|  |  |  |     ReactLogger.log(level: .info, message: "Camera stopped!") | 
					
						
							|  |  |  |     guard let onStopped = onStopped else { | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     onStopped([:]) | 
					
						
							| 
									
										
										
										
											2021-03-09 10:53:29 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   func onFrame(sampleBuffer: CMSampleBuffer) { | 
					
						
							|  |  |  |     #if VISION_CAMERA_ENABLE_FRAME_PROCESSORS | 
					
						
							|  |  |  |       if let frameProcessor = frameProcessor { | 
					
						
							|  |  |  |         // Call Frame Processor | 
					
						
							|  |  |  |         let frame = Frame(buffer: sampleBuffer, orientation: bufferOrientation) | 
					
						
							|  |  |  |         frameProcessor.call(frame) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     #endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #if DEBUG | 
					
						
							|  |  |  |       if let fpsGraph { | 
					
						
							| 
									
										
										
										
											2023-10-14 12:41:53 +02:00
										 |  |  |         DispatchQueue.main.async { | 
					
						
							|  |  |  |           fpsGraph.onTick(CACurrentMediaTime()) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |     #endif | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-07-15 09:50:39 +01:00
										 |  |  |    | 
					
						
							|  |  |  |   func onVideoChunkReady(chunk: ChunkedRecorder.Chunk) { | 
					
						
							|  |  |  |     ReactLogger.log(level: .info, message: "Chunk ready: \(chunk)") | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2024-07-15 09:55:47 +01:00
										 |  |  |     guard let onVideoChunkReady, let onInitReady else { | 
					
						
							| 
									
										
										
										
											2024-07-16 10:20:13 +01:00
										 |  |  |       ReactLogger.log(level: .warning, message: "Either onInitReady or onVideoChunkReady are not valid!") | 
					
						
							| 
									
										
										
										
											2024-07-15 09:50:39 +01:00
										 |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     switch chunk.type { | 
					
						
							|  |  |  |     case .initialization: | 
					
						
							| 
									
										
										
										
											2024-07-15 09:55:47 +01:00
										 |  |  |       onInitReady([ | 
					
						
							|  |  |  |         "filepath": chunk.url.path, | 
					
						
							|  |  |  |       ]) | 
					
						
							| 
									
										
										
										
											2024-07-16 10:46:24 +01:00
										 |  |  |     case let .data(index: index, duration: duration): | 
					
						
							|  |  |  |       var data: [String: Any] = [ | 
					
						
							| 
									
										
										
										
											2024-07-15 09:50:39 +01:00
										 |  |  |         "filepath": chunk.url.path, | 
					
						
							|  |  |  |         "index": index, | 
					
						
							| 
									
										
										
										
											2024-07-16 10:46:24 +01:00
										 |  |  |       ] | 
					
						
							|  |  |  |       if let duration { | 
					
						
							|  |  |  |         data["duration"] = duration.seconds | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       onVideoChunkReady(data) | 
					
						
							| 
									
										
										
										
											2024-07-15 09:50:39 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |   func onCodeScanned(codes: [CameraSession.Code], scannerFrame: CameraSession.CodeScannerFrame) { | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     guard let onCodeScanned = onCodeScanned else { | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     onCodeScanned([ | 
					
						
							|  |  |  |       "codes": codes.map { $0.toJSValue() }, | 
					
						
							| 
									
										
										
										
											2023-11-09 11:57:05 +01:00
										 |  |  |       "frame": scannerFrame.toJSValue(), | 
					
						
							| 
									
										
										
										
											2023-10-13 18:33:20 +02:00
										 |  |  |     ]) | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /**
 | 
					
						
							|  |  |  |    Gets the orientation of the CameraView's images (CMSampleBuffers). | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private var bufferOrientation: UIImage.Orientation { | 
					
						
							|  |  |  |     guard let cameraPosition = cameraSession.videoDeviceInput?.device.position else { | 
					
						
							|  |  |  |       return .up | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     let orientation = cameraSession.configuration?.orientation ?? .portrait | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO: I think this is wrong. | 
					
						
							|  |  |  |     switch orientation { | 
					
						
							|  |  |  |     case .portrait: | 
					
						
							|  |  |  |       return cameraPosition == .front ? .leftMirrored : .right | 
					
						
							|  |  |  |     case .landscapeLeft: | 
					
						
							|  |  |  |       return cameraPosition == .front ? .downMirrored : .up | 
					
						
							|  |  |  |     case .portraitUpsideDown: | 
					
						
							|  |  |  |       return cameraPosition == .front ? .rightMirrored : .left | 
					
						
							|  |  |  |     case .landscapeRight: | 
					
						
							|  |  |  |       return cameraPosition == .front ? .upMirrored : .down | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-02-19 16:28:05 +01:00
										 |  |  | } |