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

@@ -13,11 +13,14 @@ extension AVAudioSession {
/**
Calls [setCategory] if the given category or options are not equal to the currently set category and options.
*/
func updateCategory(_ category: AVAudioSession.Category, options: AVAudioSession.CategoryOptions = []) throws {
if self.category != category || categoryOptions.rawValue != options.rawValue {
func updateCategory(_ category: AVAudioSession.Category,
mode: AVAudioSession.Mode,
options: AVAudioSession.CategoryOptions = []) throws {
if self.category != category || categoryOptions.rawValue != options.rawValue || self.mode != mode {
ReactLogger.log(level: .info,
message: "Changing AVAudioSession category from \(self.category.rawValue) -> \(category.rawValue)")
try setCategory(category, options: options)
try setCategory(category, mode: mode, options: options)
ReactLogger.log(level: .info, message: "AVAudioSession category changed!")
}
}
}

View File

@@ -0,0 +1,30 @@
//
// AVCaptureSession+synchronizeBuffer.swift
// VisionCamera
//
// Created by Marc Rousavy on 22.11.23.
// Copyright © 2023 mrousavy. All rights reserved.
//
import AVFoundation
import Foundation
extension AVCaptureSession {
private var clock: CMClock {
if #available(iOS 15.4, *), let synchronizationClock {
return synchronizationClock
}
return masterClock ?? CMClockGetHostTimeClock()
}
/**
Synchronizes a Buffer received from this [AVCaptureSession] to the timebase of the other given [AVCaptureSession].
*/
func synchronizeBuffer(_ buffer: CMSampleBuffer, toSession to: AVCaptureSession) {
let timestamp = CMSampleBufferGetPresentationTimeStamp(buffer)
let synchronizedTimestamp = CMSyncConvertTime(timestamp, from: clock, to: to.clock)
ReactLogger.log(level: .info, message: "Synchronized Timestamp \(timestamp.seconds) -> \(synchronizedTimestamp.seconds)")
CMSampleBufferSetOutputPresentationTimeStamp(buffer, newValue: synchronizedTimestamp)
}
}

View File

@@ -0,0 +1,37 @@
//
// AVCaptureVideoDataOutput+recommendedVideoSettings.swift
// VisionCamera
//
// Created by Marc Rousavy on 22.11.23.
// Copyright © 2023 mrousavy. All rights reserved.
//
import AVFoundation
import Foundation
extension AVCaptureVideoDataOutput {
/**
Get the recommended options for an [AVAssetWriter] with the desired [RecordVideoOptions].
*/
func recommendedVideoSettings(forOptions options: RecordVideoOptions) throws -> [String: Any] {
let settings: [String: Any]?
if let videoCodec = options.codec {
settings = recommendedVideoSettings(forVideoCodecType: videoCodec, assetWriterOutputFileType: options.fileType)
} else {
settings = recommendedVideoSettingsForAssetWriter(writingTo: options.fileType)
}
guard var settings else {
throw CameraError.capture(.createRecorderError(message: "Failed to get video settings!"))
}
if let bitRate = options.bitRate {
// Convert from Mbps -> bps
let bitsPerSecond = bitRate * 1_000_000
settings[AVVideoCompressionPropertiesKey] = [
AVVideoAverageBitRateKey: NSNumber(value: bitsPerSecond),
]
}
return settings
}
}