AVAssetExportPreset 类型的 AVAssetExportSession 问题
AVAssetExportSession issue with AVAssetExportPreset type
我正在使用此扩展程序将视频文件从 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
}
}
我正在使用此扩展程序将视频文件从 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
}
}