You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:45:19