react-native-vision-camera/package/ios/Core/CameraSession+Audio.swift
Marc Rousavy cf8f3d05e3
fix: Fix Video Recording delay and improve startRecording() performance (#2192)
* fix: Add more logs

* perf: Init audio AVAssetWriter in parallel

* chore: Log Audio Session activation

* perf: Init Audio Session asynchronously

* chore: Log when category changed

* fix: Revert lazy audio initialization

* Update Podfile.lock

* Pass `sourceFormatHint` to video and audio AVAssetWriter

* fix: Remove `sourceFormatHint` from Video Asset Writer

* Use default options for Audio Asset Writer

* Update Podfile.lock

* Revert "Use default options for Audio Asset Writer"

This reverts commit e575a14c5342ddc7f9db557d5e3915328ed9e798.

* Add time logs

* fix: Don't synchronize audio buffers, they are already in sync

* shouldOptimizeForNetworkUse = false

* fix: Only update `latestTimestamp` once video buffer has been written

* perf: Use `AVAssetWriterInput` instead of `AVAssetWriterInputPixelBufferAdaptor`

* fix: Fix Audio not being synchronized with Video

* Remove logs add comments

* Format

* feat: Set `.videoRecording` AVAudioSession mode

* Refactor `startRecording()` a bit

* Format

* chore: Throw error directly instead of double-checking
2023-11-22 17:53:10 +01:00

97 lines
3.6 KiB
Swift

//
// 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,
mode: .videoRecording,
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()
ReactLogger.log(level: .info, message: "Audio Session activated!")
} 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()
ReactLogger.log(level: .info, message: "Audio Session deactivated!")
}
@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:
()
}
}
}