From 7e1e074e0f266ef94b7a7d7cf57c360e2392ae2d Mon Sep 17 00:00:00 2001 From: Loewy Date: Thu, 18 Dec 2025 13:29:31 -0800 Subject: [PATCH] force recording to stop on init write failure and fix silent failure --- package/ios/Core/CameraSession+Video.swift | 17 +++++++++++++++++ package/ios/Core/ChunkedRecorder.swift | 10 ++++++++-- package/ios/Core/RecordingSession.swift | 3 ++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/package/ios/Core/CameraSession+Video.swift b/package/ios/Core/CameraSession+Video.swift index 6c39787..825b432 100644 --- a/package/ios/Core/CameraSession+Video.swift +++ b/package/ios/Core/CameraSession+Video.swift @@ -38,11 +38,27 @@ extension CameraSession { // Callback for when new chunks are ready let onChunkReady: (ChunkedRecorder.Chunk) -> Void = { chunk in guard let delegate = self.delegate else { + ReactLogger.log(level: .warning, message: "Chunk ready but delegate is nil, dropping chunk: \(chunk)") return } delegate.onVideoChunkReady(chunk: chunk) } + // Callback for when a chunk write fails (e.g. init file write failure) + let onChunkError: (Error) -> Void = { error in + ReactLogger.log(level: .error, message: "Chunk write error, stopping recording: \(error.localizedDescription)") + // Stop recording immediately + if let session = self.recordingSession { + session.stop(clock: self.captureSession.clock) + } + // Surface error to RN + if let cameraError = error as? CameraError { + onError(cameraError) + } else { + onError(.capture(.fileError)) + } + } + // Callback for when the recording ends let onFinish = { (recordingSession: RecordingSession, status: AVAssetWriter.Status, error: Error?) in defer { @@ -98,6 +114,7 @@ extension CameraSession { let recordingSession = try RecordingSession(outputDiretory: filePath, fileType: options.fileType, onChunkReady: onChunkReady, + onChunkError: onChunkError, completion: onFinish) // Init Audio + Activate Audio Session (optional) diff --git a/package/ios/Core/ChunkedRecorder.swift b/package/ios/Core/ChunkedRecorder.swift index 2a5cd2d..3f7c933 100644 --- a/package/ios/Core/ChunkedRecorder.swift +++ b/package/ios/Core/ChunkedRecorder.swift @@ -24,12 +24,14 @@ class ChunkedRecorder: NSObject { let outputURL: URL let onChunkReady: ((Chunk) -> Void) + let onError: ((Error) -> Void)? private var chunkIndex: UInt64 = 0 - init(outputURL: URL, onChunkReady: @escaping ((Chunk) -> Void)) throws { + init(outputURL: URL, onChunkReady: @escaping ((Chunk) -> Void), onError: ((Error) -> Void)? = nil) throws { self.outputURL = outputURL self.onChunkReady = onChunkReady + self.onError = onError guard FileManager.default.fileExists(atPath: outputURL.path) else { throw CameraError.unknown(message: "output directory does not exist at: \(outputURL.path)", cause: nil) } @@ -56,8 +58,12 @@ extension ChunkedRecorder: AVAssetWriterDelegate { private func saveInitSegment(_ data: Data) { let url = outputURL.appendingPathComponent("init.mp4") - if save(data: data, url: url) { + do { + try data.write(to: url) onChunkReady(url: url, type: .initialization) + } catch { + ReactLogger.log(level: .error, message: "Failed to write init file \(url): \(error.localizedDescription)") + onError?(CameraError.capture(.fileError)) } } diff --git a/package/ios/Core/RecordingSession.swift b/package/ios/Core/RecordingSession.swift index b02c645..72a2645 100644 --- a/package/ios/Core/RecordingSession.swift +++ b/package/ios/Core/RecordingSession.swift @@ -74,12 +74,13 @@ class RecordingSession { init(outputDiretory: String, fileType: AVFileType, onChunkReady: @escaping ((ChunkedRecorder.Chunk) -> Void), + onChunkError: ((Error) -> Void)? = nil, completion: @escaping (RecordingSession, AVAssetWriter.Status, Error?) -> Void) throws { completionHandler = completion do { let outputURL = URL(fileURLWithPath: outputDiretory) - recorder = try ChunkedRecorder(outputURL: outputURL, onChunkReady: onChunkReady) + recorder = try ChunkedRecorder(outputURL: outputURL, onChunkReady: onChunkReady, onError: onChunkError) assetWriter = AVAssetWriter(contentType: UTType(fileType.rawValue)!) assetWriter.shouldOptimizeForNetworkUse = false assetWriter.outputFileTypeProfile = .mpeg4AppleHLS