SwiftUI+PhotosUI用UIImage生成3秒AVAsset报错求助
问题解决:UIImage转3秒AVAsset时AVAssetWriter写入失败
核心错误原因
你生成的outputURL是相对路径(仅UUID字符串),AVAssetWriter无法识别这种路径,必须提供应用有权限访问的绝对文件路径,且文件名需要带.mp4后缀(匹配指定的fileType)。
修复方案
1. 生成合法的绝对输出路径
使用应用的临时目录(或文档目录)作为基础路径,拼接UUID文件名和.mp4后缀,确保路径合法且有写入权限。
2. 预处理输出路径
写入前检查文件是否存在,若存在则删除;确保父目录存在(避免目录不存在导致写入失败)。
修改后的代码
获取输出URL的方法
private func getURLFromConverted(with image: UIImage) { // 获取应用临时目录的绝对路径 let tempDir = NSTemporaryDirectory() // 生成带.mp4后缀的文件名 let fileName = "\(UUID().uuidString).mp4" let outputURL = URL(fileURLWithPath: tempDir).appendingPathComponent(fileName) createVideo(from: image, outputURL: outputURL, completion: { success in if success { self.photoConvertedVideoURL = outputURL self.photoEditorViewOpened.toggle() } }) }
视频生成方法(添加路径预处理)
func createVideo(from image: UIImage, outputURL: URL, completion: @escaping (Bool) -> Void) { do { // 预处理:删除已存在的文件 let fileManager = FileManager.default if fileManager.fileExists(atPath: outputURL.path) { try fileManager.removeItem(at: outputURL) } // 确保父目录存在 let parentDir = outputURL.deletingLastPathComponent() if !fileManager.fileExists(atPath: parentDir.path) { try fileManager.createDirectory(at: parentDir, withIntermediateDirectories: true) } let writer = try AVAssetWriter(outputURL: outputURL, fileType: .mp4) let videoSettings: [String: Any] = [ AVVideoCodecKey: AVVideoCodecType.h264, AVVideoWidthKey: image.size.width, AVVideoHeightKey: image.size.height ] let writerInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings) writer.add(writerInput) let bufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput, sourcePixelBufferAttributes: nil) writer.startWriting() writer.startSession(atSourceTime: .zero) DispatchQueue.global().async { let totalFrames = 90 // 30fps * 3秒 var frameCount = 0 while frameCount < totalFrames { let frameTime = CMTimeMake(value: Int64(frameCount), timescale: 30) guard let pixelBuffer = image.toCVPixelBuffer() else { print("UIImage转CVPixelBuffer失败") completion(false) return } // 确保writerInput处于可写入状态再追加帧 while !writerInput.isReadyForMoreMediaData { Thread.sleep(forTimeInterval: 0.01) } bufferAdapter.append(pixelBuffer, withPresentationTime: frameTime) frameCount += 1 } writerInput.markAsFinished() writer.finishWriting { DispatchQueue.main.async { if writer.status == .completed { print("视频生成成功") completion(true) } else { print("视频写入失败. 状态: \(writer.status), 错误: \(writer.error?.localizedDescription ?? "未知错误")") completion(false) } } } } } catch { print("创建AVAssetWriter失败: \(error)") completion(false) } }
额外优化点
- 添加了
writerInput.isReadyForMoreMediaData的等待逻辑,避免因输入缓冲区满导致的帧丢失或写入失败。 - 明确指定文件后缀
.mp4,确保和fileType: .mp4匹配。
内容的提问来源于stack exchange,提问作者e A
相关产品推荐
相关产品推荐

