feat: New Core/ library (#1975)
				
					
				
			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
			
			
This commit is contained in:
		
							
								
								
									
										93
									
								
								package/ios/Core/CameraSession+Audio.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								package/ios/Core/CameraSession+Audio.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| // | ||||
| //  CameraSession+Audio.swift | ||||
| //  VisionCamera | ||||
| // | ||||
| //  Created by Marc Rousavy on 11.10.23. | ||||
| //  Copyright © 2023 mrousavy. All rights reserved. | ||||
| // | ||||
|  | ||||
| import AVFoundation | ||||
| import Foundation | ||||
|  | ||||
| extension CameraSession { | ||||
|   /** | ||||
|    Configures the Audio session and activates it. If the session was active it will shortly be deactivated before configuration. | ||||
|  | ||||
|    The Audio Session will be configured to allow background music, haptics (vibrations) and system sound playback while recording. | ||||
|    Background audio is allowed to play on speakers or bluetooth speakers. | ||||
|    */ | ||||
|   final func activateAudioSession() throws { | ||||
|     ReactLogger.log(level: .info, message: "Activating Audio Session...") | ||||
|  | ||||
|     do { | ||||
|       let audioSession = AVAudioSession.sharedInstance() | ||||
|  | ||||
|       try audioSession.updateCategory(AVAudioSession.Category.playAndRecord, | ||||
|                                       options: [.mixWithOthers, | ||||
|                                                 .allowBluetoothA2DP, | ||||
|                                                 .defaultToSpeaker, | ||||
|                                                 .allowAirPlay]) | ||||
|  | ||||
|       if #available(iOS 14.5, *) { | ||||
|         // prevents the audio session from being interrupted by a phone call | ||||
|         try audioSession.setPrefersNoInterruptionsFromSystemAlerts(true) | ||||
|       } | ||||
|  | ||||
|       if #available(iOS 13.0, *) { | ||||
|         // allow system sounds (notifications, calls, music) to play while recording | ||||
|         try audioSession.setAllowHapticsAndSystemSoundsDuringRecording(true) | ||||
|       } | ||||
|  | ||||
|       audioCaptureSession.startRunning() | ||||
|     } catch let error as NSError { | ||||
|       ReactLogger.log(level: .error, message: "Failed to activate audio session! Error \(error.code): \(error.description)") | ||||
|       switch error.code { | ||||
|       case 561_017_449: | ||||
|         throw CameraError.session(.audioInUseByOtherApp) | ||||
|       default: | ||||
|         throw CameraError.session(.audioSessionFailedToActivate) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   final func deactivateAudioSession() { | ||||
|     ReactLogger.log(level: .info, message: "Deactivating Audio Session...") | ||||
|  | ||||
|     audioCaptureSession.stopRunning() | ||||
|   } | ||||
|  | ||||
|   @objc | ||||
|   func audioSessionInterrupted(notification: Notification) { | ||||
|     ReactLogger.log(level: .error, message: "Audio Session Interruption Notification!") | ||||
|     guard let userInfo = notification.userInfo, | ||||
|           let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, | ||||
|           let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { | ||||
|       return | ||||
|     } | ||||
|  | ||||
|     // TODO: Add JS-Event for Audio Session interruptions? | ||||
|     switch type { | ||||
|     case .began: | ||||
|       // Something interrupted our Audio Session, stop recording audio. | ||||
|       ReactLogger.log(level: .error, message: "The Audio Session was interrupted!") | ||||
|     case .ended: | ||||
|       ReactLogger.log(level: .info, message: "The Audio Session interruption has ended.") | ||||
|       guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return } | ||||
|       let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue) | ||||
|       if options.contains(.shouldResume) { | ||||
|         // Try resuming if possible | ||||
|         if isRecording { | ||||
|           CameraQueues.audioQueue.async { | ||||
|             ReactLogger.log(level: .info, message: "Resuming interrupted Audio Session...") | ||||
|             // restart audio session because interruption is over | ||||
|             try? self.activateAudioSession() | ||||
|           } | ||||
|         } | ||||
|       } else { | ||||
|         ReactLogger.log(level: .error, message: "Cannot resume interrupted Audio Session!") | ||||
|       } | ||||
|     @unknown default: | ||||
|       () | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user