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
This commit is contained in:
Marc Rousavy
2023-11-22 17:53:10 +01:00
committed by GitHub
parent 49d58d0d0c
commit cf8f3d05e3
11 changed files with 155 additions and 110 deletions

View File

@@ -19,6 +19,7 @@ extension CameraSession {
onError: @escaping (_ error: CameraError) -> Void) {
// Run on Camera Queue
CameraQueues.cameraQueue.async {
let start = DispatchTime.now()
ReactLogger.log(level: .info, message: "Starting Video recording...")
if options.flash != .off {
@@ -59,8 +60,8 @@ extension CameraSession {
}
}
self.recordingSession = nil
self.isRecording = false
self.recordingSession = nil
ReactLogger.log(level: .info, message: "RecordingSession finished with status \(status.descriptor).")
if let error = error as NSError? {
@@ -93,47 +94,20 @@ extension CameraSession {
return
}
ReactLogger.log(level: .info, message: "File path: \(tempFilePath)")
ReactLogger.log(level: .info, message: "Will record to temporary file: \(tempFilePath)")
let tempURL = URL(string: "file://\(tempFilePath)")!
let recordingSession: RecordingSession
do {
recordingSession = try RecordingSession(url: tempURL,
fileType: options.fileType,
completion: onFinish)
} catch let error as NSError {
onError(.capture(.createRecorderError(message: error.description)))
return
}
self.recordingSession = recordingSession
// Create RecordingSession for the temp file
let recordingSession = try RecordingSession(url: tempURL,
fileType: options.fileType,
completion: onFinish)
// Init Video
guard var videoSettings = self.recommendedVideoSettings(videoOutput: videoOutput,
fileType: options.fileType,
videoCodec: options.codec),
!videoSettings.isEmpty else {
onError(.capture(.createRecorderError(message: "Failed to get video settings!")))
return
}
ReactLogger.log(level: .trace, message: "Recommended Video Settings: \(videoSettings.description)")
// Custom Video Bit Rate
if let videoBitRate = options.bitRate {
// Convert from Mbps -> bps
let bitsPerSecond = videoBitRate * 1_000_000
videoSettings[AVVideoCompressionPropertiesKey] = [
AVVideoAverageBitRateKey: NSNumber(value: bitsPerSecond),
]
}
// get pixel format (420f, 420v, x420)
let pixelFormat = videoOutput.pixelFormat
recordingSession.initializeVideoWriter(withSettings: videoSettings,
pixelFormat: pixelFormat)
// Enable/Activate Audio Session (optional)
if enableAudio {
if let audioOutput = self.audioOutput {
// Init Audio + Activate Audio Session (optional)
if enableAudio,
let audioOutput = self.audioOutput,
let audioInput = self.audioDeviceInput {
ReactLogger.log(level: .trace, message: "Enabling Audio for Recording...")
// Activate Audio Session asynchronously
CameraQueues.audioQueue.async {
do {
@@ -145,16 +119,27 @@ extension CameraSession {
// Initialize audio asset writer
let audioSettings = audioOutput.recommendedAudioSettingsForAssetWriter(writingTo: options.fileType)
recordingSession.initializeAudioWriter(withSettings: audioSettings)
recordingSession.initializeAudioWriter(withSettings: audioSettings,
format: audioInput.device.activeFormat.formatDescription)
}
}
// start recording session with or without audio.
do {
// Init Video
let videoSettings = try videoOutput.recommendedVideoSettings(forOptions: options)
recordingSession.initializeVideoWriter(withSettings: videoSettings)
// start recording session with or without audio.
try recordingSession.startAssetWriter()
self.recordingSession = recordingSession
self.isRecording = true
let end = DispatchTime.now()
ReactLogger.log(level: .info, message: "RecordingSesssion started in \(Double(end.uptimeNanoseconds - start.uptimeNanoseconds) / 1_000_000)ms!")
} catch let error as NSError {
onError(.capture(.createRecorderError(message: "RecordingSession failed to start asset writer. \(error.description)")))
if let error = error as? CameraError {
onError(error)
} else {
onError(.capture(.createRecorderError(message: "RecordingSession failed with unknown error: \(error.description)")))
}
return
}
}
@@ -208,14 +193,4 @@ extension CameraSession {
}
}
}
private func recommendedVideoSettings(videoOutput: AVCaptureVideoDataOutput,
fileType: AVFileType,
videoCodec: AVVideoCodecType?) -> [String: Any]? {
if videoCodec != nil {
return videoOutput.recommendedVideoSettings(forVideoCodecType: videoCodec!, assetWriterOutputFileType: fileType)
} else {
return videoOutput.recommendedVideoSettingsForAssetWriter(writingTo: fileType)
}
}
}