我在真实设备中的ios 13.3中也遇到了同样的问题,但它在ios 13.2模拟器中正常工作,但出现了以下错误。
Error Domain = AVFoundationErrorDomain代码= -11800“操作无法完成” UserInfo = {NSLocalizedFailureReason =发生未知错误(-17508),NSLocalizedDescription =操作无法完成,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
}
})
}
答案 0 :(得分:2)
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
答案 1 :(得分:0)
这是我用来将.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
}
答案 2 :(得分:0)
最后,我通过使用AVMutableComposition而不是直接使用AVURL资产解决了我的问题。我在AVMutableComposition中添加了音轨。
答案 3 :(得分:0)
对于任何陷入这个令人沮丧的错误的人。使用 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
}
}