Moves everything Camera related into `core/` / `Core/` so that it is better encapsulated from React Native.
Benefits:
1. Code is much better organized. Should be easier for collaborators now, and cleaner codebase for me.
2. Locking is fully atomically as you can now only configure the session through a lock/Mutex which is batch-overridable
    * On iOS, this makes Camera startup time **MUCH** faster, I measured speedups from **1.5 seconds** to only **240 milliseconds** since we only lock/commit once! 🚀 
    * On Android, this fixes a few out-of-sync/concurrency issues like "Capture Request contains unconfigured Input/Output Surface!" since it is now a single lock-operation! 💪 
3. It is easier to integrate VisionCamera outside of React Native (e.g. Native iOS Apps, NativeScript, Flutter, etc)
With this PR, VisionCamera V3 is up to **7x** faster than V2
		
	
		
			
				
	
	
		
			83 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			83 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
//
 | 
						|
//  CameraSession+Focus.swift
 | 
						|
//  VisionCamera
 | 
						|
//
 | 
						|
//  Created by Marc Rousavy on 11.10.23.
 | 
						|
//  Copyright © 2023 mrousavy. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
import AVFoundation
 | 
						|
import Foundation
 | 
						|
 | 
						|
extension CameraSession {
 | 
						|
  /**
 | 
						|
   Focuses the Camera to the specified point. The point must be in the Camera coordinate system, so {0...1} on both axis.
 | 
						|
   */
 | 
						|
  func focus(point: CGPoint) throws {
 | 
						|
    guard let device = videoDeviceInput?.device else {
 | 
						|
      throw CameraError.session(SessionError.cameraNotReady)
 | 
						|
    }
 | 
						|
    if !device.isFocusPointOfInterestSupported {
 | 
						|
      throw CameraError.device(DeviceError.focusNotSupported)
 | 
						|
    }
 | 
						|
 | 
						|
    do {
 | 
						|
      try device.lockForConfiguration()
 | 
						|
      defer {
 | 
						|
        device.unlockForConfiguration()
 | 
						|
      }
 | 
						|
 | 
						|
      // Set Focus
 | 
						|
      if device.isFocusPointOfInterestSupported {
 | 
						|
        device.focusPointOfInterest = point
 | 
						|
        device.focusMode = .autoFocus
 | 
						|
      }
 | 
						|
 | 
						|
      // Set Exposure
 | 
						|
      if device.isExposurePointOfInterestSupported {
 | 
						|
        device.exposurePointOfInterest = point
 | 
						|
        device.exposureMode = .autoExpose
 | 
						|
      }
 | 
						|
 | 
						|
      // Remove any existing listeners
 | 
						|
      NotificationCenter.default.removeObserver(self,
 | 
						|
                                                name: NSNotification.Name.AVCaptureDeviceSubjectAreaDidChange,
 | 
						|
                                                object: nil)
 | 
						|
 | 
						|
      // Listen for focus completion
 | 
						|
      device.isSubjectAreaChangeMonitoringEnabled = true
 | 
						|
      NotificationCenter.default.addObserver(self,
 | 
						|
                                             selector: #selector(subjectAreaDidChange),
 | 
						|
                                             name: NSNotification.Name.AVCaptureDeviceSubjectAreaDidChange,
 | 
						|
                                             object: nil)
 | 
						|
    } catch {
 | 
						|
      throw CameraError.device(DeviceError.configureError)
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @objc
 | 
						|
  func subjectAreaDidChange(notification _: NSNotification) {
 | 
						|
    guard let device = videoDeviceInput?.device else {
 | 
						|
      return
 | 
						|
    }
 | 
						|
 | 
						|
    try? device.lockForConfiguration()
 | 
						|
    defer {
 | 
						|
      device.unlockForConfiguration()
 | 
						|
    }
 | 
						|
 | 
						|
    // Reset Focus to continuous/auto
 | 
						|
    if device.isFocusPointOfInterestSupported {
 | 
						|
      device.focusMode = .continuousAutoFocus
 | 
						|
    }
 | 
						|
 | 
						|
    // Reset Exposure to continuous/auto
 | 
						|
    if device.isExposurePointOfInterestSupported {
 | 
						|
      device.exposureMode = .continuousAutoExposure
 | 
						|
    }
 | 
						|
 | 
						|
    // Disable listeners
 | 
						|
    device.isSubjectAreaChangeMonitoringEnabled = false
 | 
						|
  }
 | 
						|
}
 |