收到开发者中心发来的邮件,这个 bug 已经确定并且苹果工程师正在着手解决。

问题

最近在开发一个短视频 app,既然是新项目,而且 Swift 已经基本达到可用的状态(大雾),所以理所当然的选择了 Swift 作为开发语言。目前一切还算顺利,不过这几天倒是被一个问题困扰了很久,写了很多代码来测试复现问题,最终找出了可能存在问题的一行代码。下面是出问题代码的相关片段:

        var reversedAudioAssetTrack: AVAssetTrack? = nil
        var reversedVideoAssetTrack: AVAssetTrack? = nil
        
        separator.separateReversed { (audioUrl, videoUrl) in
            let composition: AVMutableComposition = AVMutableComposition()
            let videoCompositionTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
            let audioCompositionTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)
            
            // if let audio = audioUrl {
            //    let reversedAudioAsset = AVURLAsset(url: audio)
            //    reversedAudioAssetTrack = reversedAudioAsset.tracks(withMediaType: AVMediaTypeAudio).first
            //}
            let reversedAudioAsset = AVURLAsset(url: audioUrl!)
            reversedAudioAssetTrack = reversedAudioAsset.tracks(withMediaType: AVMediaTypeAudio).first

            
            // if let video = videoUrl {
            //    let reversedVideoAsset = AVURLAsset(url: video)
            //    reversedVideoAssetTrack = reversedVideoAsset.tracks(withMediaType: AVMediaTypeVideo).first
            // }
            let reversedVideoAsset = AVURLAsset(url: videoUrl!)
            reversedVideoAssetTrack = reversedVideoAsset.tracks(withMediaType: AVMediaTypeVideo).first
            
            do {
                if let reversedVideoAssetTrack = reversedVideoAssetTrack {
                    try videoCompositionTrack.insertTimeRange(reversedVideoAssetTrack.timeRange, of: reversedVideoAssetTrack, at: kCMTimeZero)
                }
                if let reversedAudioAssetTrack = reversedAudioAssetTrack {
                    try audioCompositionTrack.insertTimeRange(reversedAudioAssetTrack.timeRange, of: reversedAudioAssetTrack, at: kCMTimeZero)
                }
            } catch let error {
                print("\(error)")
                return
            }
            
            let export: AVAssetExportSession? = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)
            export?.outputURL = self.outputUrl
            export?.outputFileType = AVFileTypeQuickTimeMovie
            export?.shouldOptimizeForNetworkUse = true
            export?.exportAsynchronously(completionHandler: {
                if export?.status == .completed {
                    Swift.print(export?.outputURL ?? "nil")
                    export?.outputURL?.saveVideoToAlbum()
                } else {
                    print("failed to export")
                }
            })
        }

出现问题的就是注释掉的几行代码。出于安全考虑,对于可能为空的 audioUrlvideoUrl 我进行了 optional binding 的操作,并在非空的情况创建 AVAsset 并取出对应的 audio track 或者 video track,然而这样操作后,程序运行就会在尝试 insert asset trackcomposition track 的时候异常,打印如下的错误:

Error Domain=AVFoundationErrorDomain Code=-11800 “The operation could not be completed” UserInfo={NSUnderlyingError=0x17425c260 {Error Domain=NSOSStatusErrorDomain Code=-12780 “(null)”}, NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed}

osstatus.com 查阅到 -11800 是一个未知错误,结合之前的经验,猜测是插入的 time range 有错误,但是在手动修改 time range 到一个固定值后(start 为 kCMTimeZero,duration 小于视频长度)依然出现问题。

由于注释与未注释的代码仅有对 URLoptional binding 的区别,所以一时之间也并不清楚问题的根本原因。目前,已经在 bugs.swift.org 提了相关的 issue,并且将可复现的代码上传到了 GitHub,期待能够尽快得到回复。


这个问题很可能是我作为一个初学者犯下的一个没意识到的错误,当 issue 被解决的时候我会更新贴出结果。如果有读者恰好碰到相同的问题并且有明确的解答还望不吝赐教。