Swift音视频合并报错AVFoundationErrorDomain Code=-11838排查
合并视频与音频时触发错误:
"failed Error Domain=AVFoundationErrorDomain Code=-11838 "Operation Stopped" UserInfo=0x17da4230 {NSLocalizedDescription=Operation Stopped, NSLocalizedFailureReason=The operation is not supported for this media.}"
每次调用mergeVideoWithAudio都会进入Failure回调,怀疑是媒体格式问题,相关代码及调用示例如下:
代码实现
func mergeVideoWithAudio(videoUrl: URL, audioUrl: URL, success: @escaping ((URL) -> Void), failure: @escaping ((Error?) -> Void)) { let mixComposition: AVMutableComposition = AVMutableComposition() var mutableCompositionVideoTrack: [AVMutableCompositionTrack] = [] var mutableCompositionAudioTrack: [AVMutableCompositionTrack] = [] let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction() let aVideoAsset: AVAsset = AVAsset(url: videoUrl) let aAudioAsset: AVAsset = AVAsset(url: audioUrl) if let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) { mutableCompositionVideoTrack.append(videoTrack) mutableCompositionAudioTrack.append(audioTrack) if let aVideoAssetTrack: AVAssetTrack = aVideoAsset.tracks(withMediaType: .video).first, let aAudioAssetTrack: AVAssetTrack = aAudioAsset.tracks(withMediaType: .audio).first { do { try mutableCompositionVideoTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aVideoAssetTrack, at: CMTime.zero) try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: CMTime.zero) videoTrack.preferredTransform = aVideoAssetTrack.preferredTransform } catch{ print(error) } totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero,duration: videoTrack.timeRange.duration) } } let mutableVideoComposition: AVMutableVideoComposition = AVMutableVideoComposition() mutableVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30) mutableVideoComposition.renderSize = CGSize(width: 480, height: 640) if let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first { let outputURL = URL(fileURLWithPath: documentsPath).appendingPathComponent("\(\"fileName\").mp4") do { if FileManager.default.fileExists(atPath: outputURL.path) { try FileManager.default.removeItem(at: outputURL) } } catch { } print(outputURL, "outputURL.....") if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) { exportSession.outputURL = NSURL.fileURL(withPath: outputURL.path) exportSession.outputFileType = AVFileType.mov exportSession.shouldOptimizeForNetworkUse = true exportSession.exportAsynchronously(completionHandler: { switch exportSession.status { case .failed: if let _error = exportSession.error { print("failedddd",_error) failure(_error) } case .cancelled: if let _error = exportSession.error { print("cancellled") failure(_error) } default: print("finished") success(outputURL) } }) } else { print("failurrrrrrrr") failure(nil) } } }
调用示例
let vidUrl = URL(string: "file:///private/var/mobile/Containers/Data/PluginKitPlugin/C6BD4529-DF11-4A67-B56E-C3A841D86D75/tmp/trim.CC7EABA8-08EC-4C33-8E6A-E85A5EBCFB64.MOV")! let audioUrl = URL(string: "file:///var/mobile/Containers/Data/Application/9115DCFE-4134-48AD-84BF-452AC0D33278/Documents/y2matecom-ChallaOfficialFullVideoKhanSaabAYMediaRecordsLatestPunjabiSongs2016.mp3")! mergeVideoWithAudio(videoUrl: vidUrl, audioUrl: audioUrl) { resultUrl in print("sucessssssssss",resultUrl) } failure: { error in print(error?.localizedDescription,"hgjjhfghdg") }
1. 输出文件格式与扩展名不匹配
代码中设置exportSession.outputFileType = AVFileType.mov,但输出URL的扩展名是.mp4,容器格式和文件扩展名不一致会导致AVFoundation拒绝处理。
解决: 统一格式,要么将输出扩展名改为.mov,要么将outputFileType设为AVFileType.mp4(MP3音频在MP4容器中兼容,可正常使用)。
2. 未等待AVAsset加载完成
AVAsset初始化后需异步加载元数据(如轨道、时长),直接访问tracks(withMediaType:)可能返回空或错误信息,导致插入时间范围失败。
解决: 使用loadValuesAsynchronously(forKeys:completionHandler:)确保Asset加载完成后再处理,示例如下:
let videoKeys = ["tracks"] aVideoAsset.loadValuesAsynchronously(forKeys: videoKeys) { var videoError: NSError? let videoStatus = aVideoAsset.statusOfValue(forKey: "tracks", error: &videoError) let audioKeys = ["tracks"] aAudioAsset.loadValuesAsynchronously(forKeys: audioKeys) { var audioError: NSError? let audioStatus = aAudioAsset.statusOfValue(forKey: "tracks", error: &audioError) DispatchQueue.main.async { if videoStatus == .loaded && audioStatus == .loaded { // 执行后续轨道合并逻辑 } else { failure(videoError ?? audioError) } } } }
3. 音频轨道插入时间范围逻辑错误
代码中音频轨道插入时长使用的是视频轨道的timeRange.duration,若视频轨道存在裁剪或音频时长与视频不匹配,会导致时间范围不合法。应使用视频Asset的总时长而非轨道的局部时长。
解决: 替换为视频Asset的总时长:
try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAsset.duration), of: aAudioAssetTrack, at: CMTime.zero)
4. 未关联VideoComposition到ExportSession
代码中创建了mutableVideoComposition但未赋值给exportSession.videoComposition,会导致AVFoundation无法正确渲染视频帧。
解决: 添加关联代码:
exportSession.videoComposition = mutableVideoComposition
5. 检查Export Preset兼容性
AVAssetExportPresetHighestQuality并非兼容所有格式组合,若仅需合并轨道无需转码,可尝试使用AVAssetExportPresetPassthrough提升兼容性。
内容的提问来源于stack exchange,提问作者Ayaz Hassan

