From fb2156ec397f573fc7beac56de6b8975f5831500 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Sat, 11 Jun 2022 11:15:24 +0200 Subject: [PATCH] fix: Asset Writer Video-Audio Sync (#1075) * fix: Start asset writer session on first frame * fix: Remove debug log * fix: Reset dev team --- example/ios/Podfile.lock | 4 ++-- .../project.pbxproj | 4 ++-- ios/CameraView+RecordVideo.swift | 4 ++-- ios/RecordingSession.swift | 21 ++++++++----------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 4a70f21b73..5e0009f376 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -324,7 +324,7 @@ PODS: - React - RNVectorIcons (8.1.0): - React-Core - - VisionCamera (2.13.0): + - VisionCamera (2.13.3): - React - React-callinvoker - React-Core @@ -504,7 +504,7 @@ SPEC CHECKSUMS: RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19 RNStaticSafeAreaInsets: 6103cf09647fa427186d30f67b0f5163c1ae8252 RNVectorIcons: 31cebfcf94e8cf8686eb5303ae0357da64d7a5a4 - VisionCamera: 9959a41d3edc36b37e8361a2e6cbc05714f0689b + VisionCamera: 7bcf3a81533a1c9ad13930804377ad13a03fcded Yoga: e7dc4e71caba6472ff48ad7d234389b91dadc280 PODFILE CHECKSUM: 29b1752e05601e9867644e58ce0ed8b9106be6cb diff --git a/example/ios/VisionCameraExample.xcodeproj/project.pbxproj b/example/ios/VisionCameraExample.xcodeproj/project.pbxproj index cf83259d52..87eb3b3109 100644 --- a/example/ios/VisionCameraExample.xcodeproj/project.pbxproj +++ b/example/ios/VisionCameraExample.xcodeproj/project.pbxproj @@ -456,7 +456,7 @@ COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 "; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -517,7 +517,7 @@ COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 "; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/ios/CameraView+RecordVideo.swift b/ios/CameraView+RecordVideo.swift index 7c1903bcda..799a611aac 100644 --- a/ios/CameraView+RecordVideo.swift +++ b/ios/CameraView+RecordVideo.swift @@ -140,9 +140,9 @@ extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAud // start recording session with or without audio. do { - try recordingSession.start() + try recordingSession.startAssetWriter() } catch let error as NSError { - callback.reject(error: .capture(.createRecorderError(message: "RecordingSession failed to start writing.")), cause: error) + callback.reject(error: .capture(.createRecorderError(message: "RecordingSession failed to start asset writer.")), cause: error) return } self.isRecording = true diff --git a/ios/RecordingSession.swift b/ios/RecordingSession.swift index 16a7ada9ba..a13b548706 100644 --- a/ios/RecordingSession.swift +++ b/ios/RecordingSession.swift @@ -32,6 +32,7 @@ class RecordingSession { private var initialTimestamp: CMTime? private var latestTimestamp: CMTime? + private var hasStartedWritingSession = false private var hasWrittenFirstVideoFrame = false private var isFinishing = false @@ -111,7 +112,7 @@ class RecordingSession { /** Start the Asset Writer(s). If the AssetWriter failed to start, an error will be thrown. */ - func start() throws { + func startAssetWriter() throws { ReactLogger.log(level: .info, message: "Starting Asset Writer(s)...") let success = assetWriter.startWriting() @@ -119,10 +120,6 @@ class RecordingSession { ReactLogger.log(level: .error, message: "Failed to start Asset Writer(s)!") throw RecordingSessionError.failedToStartSession } - - initialTimestamp = CMTime(seconds: CACurrentMediaTime(), preferredTimescale: 1_000_000_000) - assetWriter.startSession(atSourceTime: initialTimestamp!) - ReactLogger.log(level: .info, message: "Started RecordingSession at \(initialTimestamp!.seconds) seconds.") } /** @@ -138,11 +135,6 @@ class RecordingSession { ReactLogger.log(level: .error, message: "Frame arrived, but sample buffer is not ready!") return } - guard let initialTimestamp = initialTimestamp else { - ReactLogger.log(level: .error, - message: "A frame arrived, but initialTimestamp was nil. Is this RecordingSession running?") - return - } latestTimestamp = timestamp @@ -161,10 +153,15 @@ class RecordingSession { ReactLogger.log(level: .error, message: "Failed to get the CVImageBuffer!") return } + // Start the writing session before we write the first video frame + if !hasStartedWritingSession { + assetWriter.startSession(atSourceTime: timestamp) + ReactLogger.log(level: .info, message: "Started RecordingSession at \(timestamp.seconds) seconds.") + hasStartedWritingSession = true + } bufferAdaptor.append(imageBuffer, withPresentationTime: timestamp) if !hasWrittenFirstVideoFrame { hasWrittenFirstVideoFrame = true - ReactLogger.log(level: .warning, message: "VideoWriter: First frame arrived \((initialTimestamp - timestamp).seconds) seconds late.") } case .audio: guard let audioWriter = audioWriter else { @@ -174,7 +171,7 @@ class RecordingSession { if !audioWriter.isReadyForMoreMediaData { return } - if !hasWrittenFirstVideoFrame { + if !hasWrittenFirstVideoFrame || !hasStartedWritingSession { // first video frame has not been written yet, so skip this audio frame. return }