From f86b1cf6a14fbaa3f18ab3d99ea5f7e727b703a1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 14 Dec 2025 02:34:22 +0000 Subject: [PATCH] fix(camera): modernize mp4 export --- .../ios/Sources/Camera/CameraController.swift | 38 ++++++++++------- .../Clawdis/CameraCaptureService.swift | 42 ++++++++++++------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/apps/ios/Sources/Camera/CameraController.swift b/apps/ios/Sources/Camera/CameraController.swift index 1d082077b..18f7930c6 100644 --- a/apps/ios/Sources/Camera/CameraController.swift +++ b/apps/ios/Sources/Camera/CameraController.swift @@ -199,25 +199,35 @@ actor CameraController { } private nonisolated static func exportToMP4(inputURL: URL, outputURL: URL) async throws { - let asset = AVAsset(url: inputURL) + let asset = AVURLAsset(url: inputURL) guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else { throw CameraError.exportFailed("Failed to create export session") } - exporter.outputURL = outputURL - exporter.outputFileType = .mp4 exporter.shouldOptimizeForNetworkUse = true - try await withCheckedThrowingContinuation(isolation: nil) { cont in - exporter.exportAsynchronously { - switch exporter.status { - case .completed: - cont.resume(returning: ()) - case .failed: - cont.resume(throwing: exporter.error ?? CameraError.exportFailed("Export failed")) - case .cancelled: - cont.resume(throwing: CameraError.exportFailed("Export cancelled")) - default: - cont.resume(throwing: CameraError.exportFailed("Export did not complete")) + if #available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) { + do { + try await exporter.export(to: outputURL, as: .mp4) + return + } catch { + throw CameraError.exportFailed(error.localizedDescription) + } + } else { + exporter.outputURL = outputURL + exporter.outputFileType = .mp4 + + try await withCheckedThrowingContinuation(isolation: nil) { cont in + exporter.exportAsynchronously { + switch exporter.status { + case .completed: + cont.resume(returning: ()) + case .failed: + cont.resume(throwing: exporter.error ?? CameraError.exportFailed("Export failed")) + case .cancelled: + cont.resume(throwing: CameraError.exportFailed("Export cancelled")) + default: + cont.resume(throwing: CameraError.exportFailed("Export did not complete")) + } } } } diff --git a/apps/macos/Sources/Clawdis/CameraCaptureService.swift b/apps/macos/Sources/Clawdis/CameraCaptureService.swift index a0dfa88f4..9695e8852 100644 --- a/apps/macos/Sources/Clawdis/CameraCaptureService.swift +++ b/apps/macos/Sources/Clawdis/CameraCaptureService.swift @@ -200,29 +200,39 @@ actor CameraCaptureService { } private nonisolated static func exportToMP4(inputURL: URL, outputURL: URL) async throws { - let asset = AVAsset(url: inputURL) + let asset = AVURLAsset(url: inputURL) guard let export = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetMediumQuality) else { throw CameraError.exportFailed("Failed to create export session") } - export.outputURL = outputURL - export.outputFileType = .mp4 export.shouldOptimizeForNetworkUse = true - await withCheckedContinuation { cont in - export.exportAsynchronously { - cont.resume() + if #available(macOS 15.0, *) { + do { + try await export.export(to: outputURL, as: .mp4) + return + } catch { + throw CameraError.exportFailed(error.localizedDescription) } - } + } else { + export.outputURL = outputURL + export.outputFileType = .mp4 - switch export.status { - case .completed: - return - case .failed: - throw CameraError.exportFailed(export.error?.localizedDescription ?? "export failed") - case .cancelled: - throw CameraError.exportFailed("export cancelled") - default: - throw CameraError.exportFailed("export did not complete (\(export.status.rawValue))") + await withCheckedContinuation { cont in + export.exportAsynchronously { + cont.resume() + } + } + + switch export.status { + case .completed: + return + case .failed: + throw CameraError.exportFailed(export.error?.localizedDescription ?? "export failed") + case .cancelled: + throw CameraError.exportFailed("export cancelled") + default: + throw CameraError.exportFailed("export did not complete (\(export.status.rawValue))") + } } } }