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

Swift音视频合并报错AVFoundationErrorDomain Code=-11838排查

问题: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 09:05:24