AVAssetExportSession转MP4为M4A:模拟器成功真机失败求助
MP4转M4A真机失败的问题解决思路
问题回顾
在iOS应用中使用AVAssetExportSession将MP4视频转换为M4A音频格式时,模拟器(iOS15.5/16.1)运行正常,但iPhone7(iOS15.7.1)及iOS16.1真机均失败,报错为AVFoundationErrorDomain Code=-11800,底层错误码-16979。输入URL来自iCloud Drive的文件应用。
转换代码如下:
let outputUrl = URL(fileURLWithPath: NSTemporaryDirectory() + "out.m4a") if FileManager.default.fileExists(atPath: outputUrl.path) { try? FileManager.default.removeItem(atPath: outputUrl.path) } let asset = AVURLAsset(url: inputUrl) // 试过`AVAssetExportPresetAppleM4A`预设,结果一样 let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough)! exportSession.outputFileType = AVFileType.m4a exportSession.outputURL = outputUrl await exportSession.export() switch exportSession.status { case .completed: return outputUrl default: // 状态值为4,对应.failed print("Status: \(exportSession.status)") throw exportSession.error! }
完整报错信息:
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSUnderlyingError=0x2808f30c0 {Error Domain=NSOSStatusErrorDomain Code=-16979 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-16979), NSLocalizedRecoverySuggestion=XXXXDEFAULTVALUEXXXX, NSURL=file:///private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/Downloads/%E3%81%8A%E3%81%97%E3%82%83%E3%81%B8%E3%82%99%E3%82%8A%E3%81%B2%E3%82%8D%E3%82%86%E3%81%8D.mp4, NSLocalizedDescription=The operation could not be completed}
可能的原因及解决方法
1. 文件权限问题(最常见)
从UIDocumentPickerViewController获取的iCloud文件URL需要持久化安全范围权限,真机沙箱机制严格,没有权限会导致文件访问失败,模拟器因沙箱限制宽松不会触发该问题。
解决代码:
// 获取inputUrl后,保存书签数据以持久化权限 if let bookmarkData = try? inputUrl.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) { UserDefaults.standard.set(bookmarkData, forKey: "VideoFileBookmark") } // 后续使用URL时,恢复权限并访问 if let bookmarkData = UserDefaults.standard.data(forKey: "VideoFileBookmark") { var isStale = false guard let restoredUrl = try? URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) else { return } // 开始访问安全范围资源 restoredUrl.startAccessingSecurityScopedResource() // 用restoredUrl替代原inputUrl进行AVAsset操作 let asset = AVURLAsset(url: restoredUrl) // 操作完成后必须停止访问 defer { restoredUrl.stopAccessingSecurityScopedResource() } }
2. 输出URL生成不规范
直接拼接字符串生成临时文件URL可能存在编码或路径识别问题,改用系统API生成更可靠:
let tempDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let outputUrl = tempDir.appendingPathComponent("out.m4a")
3. 导出预设选择不当
AVAssetExportPresetPassthrough要求原视频的音频编码与M4A完全兼容,若原MP4音频编码不支持透传会直接失败。建议改用AVAssetExportPresetAppleM4A,该预设会自动处理编码转换:
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A)! exportSession.outputFileType = .m4a // 可选:指定音频比特率优化输出质量 exportSession.audioBitRate = 128000
4. 检查原视频的音频轨道
部分MP4可能无音频轨道或音频轨道损坏,先验证资产的音频轨道有效性:
let asset = AVURLAsset(url: inputUrl) let audioTracks = asset.tracks(withMediaType: .audio) if audioTracks.isEmpty { print("视频无有效音频轨道") return }
验证步骤
- 优先添加权限持久化代码,确保能正常访问iCloud文件;
- 替换输出URL的生成方式;
- 切换到
AVAssetExportPresetAppleM4A预设; - 检查原视频是否包含有效音频轨道。
内容的提问来源于stack exchange,提问作者Toru
相关产品推荐
相关产品推荐

