AVAssetExportSession 给我 AVFoundationErrorDomain Code=-11800

AVAssetExportSession gives me AVFoundationErrorDomain Code=-11800

我在 ios 13.3 中遇到了同样的问题,在真实设备中它在 ios 13.2 模拟器中工作但给出了以下错误。

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

这是我要将 .mov 文件转换为 mp4 的代码。

class func encodeVideo(at videoURL: String, completionHandler: ((URL?, Error?) -> Void)?)  {  
    let avAsset = AVURLAsset(url: URL.init(fileURLWithPath: videoURL), options: nil)  

    let startDate = Date()  

    //Create Export session  
    guard let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) else {  
        completionHandler?(nil, nil)  
        return  
    }  

    //Creating temp path to save the converted video  
    let filename = "Video_\(Date().timeIntervalSince1970).mp4"  
      // Below Folder Path used tor getting directory path  
    let strfilePath = (FolderPath.temporaryDirectory.getDirectoryPath as NSString).appendingPathComponent(filename)  
    let filePath = URL.init(fileURLWithPath: strfilePath)  

    //Check if the file already exists then remove the previous file  
    if FileManager.default.fileExists(atPath: filePath.path) {  
        do {  
            try FileManager.default.removeItem(at: filePath)  
        } catch {  
            completionHandler?(nil, error)  
        }  
    }  

    exportSession.outputURL = filePath  
    exportSession.outputFileType = AVFileType.mp4  
    exportSession.shouldOptimizeForNetworkUse = true  
    let start = CMTimeMakeWithSeconds(0.0, preferredTimescale: 0)  
    let range = CMTimeRangeMake(start: start, duration: avAsset.duration)  
    exportSession.timeRange = range  

    exportSession.exportAsynchronously(completionHandler: {() -> Void in  
        switch exportSession.status {  
        case .failed:  
            print(exportSession.error ?? "NO ERROR")  
            completionHandler?(nil, exportSession.error)  
        case .cancelled:  
            print("Export canceled")  
            completionHandler?(nil, nil)  
        case .completed:  
            //Video conversion finished  
            let endDate = Date()  

            let time = endDate.timeIntervalSince(startDate)  
            print(time)  
            print("Successful!")  
            print(exportSession.outputURL ?? "NO OUTPUT URL")  
            completionHandler?(exportSession.outputURL, nil)  

            default: break  
        }  

    })  
}  
let strfilePath = (FolderPath.temporaryDirectory.getDirectoryPath as NSString).appendingPathComponent(filename)  

您不能直接存储在该文件夹中,但您需要将文件存储在子文件夹中,例如像这样:

let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL

let strfilePath = documentDirectoryURL.appendingPathComponent("Subfolder/filename.mp4") as URL  

进一步你可以阅读这个article

这是我用来将 .mov 转换为 .mp4 的代码

  var outputURL: URL!

   func exportVideo(key:String, inputurl: URL, presetName: String, outputFileType: AVFileType = .mp4, fileExtension: String = "mp4", then completion: @escaping (URL?) -> Void) {

        let asset = AVAsset(url: inputurl)

        outputURL = FileManager.default.temporaryDirectory.appendingPathComponent(key)

        if let session = AVAssetExportSession(asset: asset, presetName: presetName) {
            session.outputURL = outputURL
            session.outputFileType = outputFileType

            session.shouldOptimizeForNetworkUse = true
            session.exportAsynchronously {
                switch session.status {
                case .completed:
                    completion(self.outputURL)
                case .cancelled:
                    debugPrint("Video export cancelled.")
                    completion(nil)
                case .failed:
                    let errorMessage = session.error?.localizedDescription ?? "n/a"
                    debugPrint("Video export failed with error: \(errorMessage)")
                    completion(nil)
                default:
                    break
                }
            }
        } else {
            completion(nil)
        }
    }

然后我调用这个函数来获取转换文件的输出URL并使用它

exportVideo(key: key, inputurl: path, presetName: AVAssetExportPresetHighestQuality, outputFileType: .mp4, fileExtension: "mp4") { (outputURL) in 

    // do whatever with the file here
}

最后,我通过使用 AVMutableComposition 而不是直接使用 AVURL 资产来解决我的问题。我在 AVMutableComposition 中添加音频和视频轨道。

对于陷入这个令人沮丧的错误的任何人。使用 AVmutablecomposition 作为导出资产。

剪辑视频的示例代码:

let manager = FileManager.default
        guard let documentDirectory = try?  manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)  else { print("TRIM Failed to access directory")
            return}
     
       
        
        
        let mediaType = "mp4"
        if mediaType == kUTTypeMovie as String || mediaType == "mp4" as String {
               let asset = AVAsset(url:videoURL!)
            let length = Float(asset.duration.value) / Float(asset.duration.timescale)
               print("TRIM video length: \(length) seconds")
            
       
            let composition = AVMutableComposition()
            let audioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
            let videoTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)!
            
     
            var outURL_speed=documentDirectory.appendingPathComponent("output")
            var outputURL = documentDirectory.appendingPathComponent("output")
            
                    do {
                        
                     
                        try audioTrack.insertTimeRange(CMTimeRangeFromTimeToTime(start: self.startTime, end: self.endTime), of: asset.tracks(withMediaType: AVMediaType.audio)[0], at: CMTime.zero)
                        try videoTrack.insertTimeRange(CMTimeRangeFromTimeToTime(start: self.startTime, end: self.endTime), of: asset.tracks(withMediaType: AVMediaType.video)[0], at: CMTime.zero)
                     
                        try manager.createDirectory(at: outputURL, withIntermediateDirectories: true, attributes: nil)
                        outputURL = outputURL.appendingPathComponent("preVideo-\(self.postID).\(mediaType)")
                        outURL_speed = outURL_speed.appendingPathComponent("Video-\(self.postID).\(mediaType)")
                        print("TRIM output dir: \(outputURL)")
                    }catch let error {
                        print(error)
                    }

            _ = try? manager.removeItem(at: outputURL)
            
            
        
            
            guard let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else {return}
            
            exportSession.outputURL = outputURL
            exportSession.shouldOptimizeForNetworkUse = true
            exportSession.outputFileType = .mp4
            let timeRange = CMTimeRange(start: self.startTime, end: self.endTime)
            exportSession.timeRange = timeRange
            
            exportSession.exportAsynchronously{
                switch exportSession.status {
                case .completed:
                    print("TRIM exported at \(outputURL)")
                    self.changeSpeed(url: outputURL,outUrl:outURL_speed)
                case .failed:
                    print("TRIM failed \(exportSession.error)")

                case .cancelled:
                    print("TRIM cancelled \(exportSession.error)")

                default: break
                }
            }