ios - AVAssetExportPreset 类型的 AVAssetExportSession 问题

标签 ios swift avasset

我正在使用此扩展程序将 AVAsset 中的视频文件保存到 tmp 文件夹中。问题是,当我使用 AVAssetExportPresetHighestQuality 类型的视频文件时,由于这个原因无法保存:

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

有时甚至在我使用 AVAssetExportPresetHighestQuality 时它也会保存视频,但顺序是随机的。

extension AVAsset {

    func write(to url: URL, success: @escaping () -> (), failure: @escaping (Error) -> ()) {
        guard let exportSession = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetMediumQuality) else {
            let error = NSError(domain: "domain", code: 0, userInfo: nil)
            failure(error)

            return
        }

        exportSession.outputFileType = AVFileTypeMPEG4
        exportSession.outputURL = url

        exportSession.exportAsynchronously {
            switch exportSession.status {
            case .completed:
                success()
            case .unknown, .waiting, .exporting, .failed, .cancelled:
                let error = NSError(domain: "domain", code: 0, userInfo: nil)
                failure(error)
            }
        }
    }
}

最佳答案

此问题与 AVAsset 组件的错误长度有关。出于某种原因,AVAsset 轨道的视频和音频轨道的持续时间不同,这是主要问题。

为了解决这个问题,我使用了 AVAsset 的自定义扩展。此功能将根据视频和音频轨道创建新的 AVAsset,并解决持续时间问题。所以从normalizingMediaDuration()获取的AVAsset可以成功导出。

extension AVAsset {
    func normalizingMediaDuration() -> AVAsset? {
        let mixComposition : AVMutableComposition = AVMutableComposition()
        var mutableCompositionVideoTrack : [AVMutableCompositionTrack] = []
        var mutableCompositionAudioTrack : [AVMutableCompositionTrack] = []
        let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()

        guard let video = tracks(withMediaType: AVMediaTypeVideo).first else {
            return nil
        }

        guard let audio = tracks(withMediaType: AVMediaTypeAudio).first else {
            return nil
        }

        mutableCompositionVideoTrack.append(mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid))
        mutableCompositionAudioTrack.append(mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid))

        let duration = video.timeRange.duration.seconds > audio.timeRange.duration.seconds ? audio.timeRange.duration : video.timeRange.duration

        do{
            try mutableCompositionVideoTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero,duration), of: video, at: kCMTimeZero)
            try mutableCompositionAudioTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, duration), of: audio, at: kCMTimeZero)
        }catch{
            return nil
        }

        totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,duration)

        return mixComposition
    }
}

关于ios - AVAssetExportPreset 类型的 AVAssetExportSession 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46193086/

相关文章:

iphone - 从 NSOperationQueue 中取消 NSOperation 导致崩溃

javascript - Html5音频ios播放事件

swift - 如何在 Swift 中分配和解包可选数组对象?

ios - 如何使用按钮放大/缩小 GoogleMap

ios - 如何监听 AVPlayer "on seek"事件?

ios - Swift:AVAssetResourceLoader 不播放来自 URL 的音频

iphone - ASIHTTPRequest completionBlock + ARC

ios - 我们如何在 iPhone 中检测人脸

ios - 单击按钮并更改其图像时的 UITableView Swift 4

ios - GPUImage initWithAsset 只显示最后一帧