cf8f3d05e3
* 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
97 lines
3.6 KiB
Swift
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:
|
|
()
|
|
}
|
|
}
|
|
}
|