通过AVAssetExportSession导出mp4失败 [英] Exporting mp4 through AVAssetExportSession fails
问题描述
我开始说我花了很多时间搜索文档,在这里和其他地方的帖子,但是我无法找到解决该问题的方法.
I start saying that I spent a lot of time searching through documentation, posts here and somewhere else, but I can't figure out the solution for this problem.
我正在使用AVAssetExportSession
导出存储在AVAsset
实例中的.mp4
文件.
我要做的是:
I'm using AVAssetExportSession
for exporting an .mp4
file stored in a AVAsset
instance.
What I do is:
- 我检查
AVAsset
的isExportable
属性 - 然后我得到一个与
AVAsset
实例兼容的exportPresets
数组 - 我使用
AVAssetExportPreset1920x1080
,或者,如果不存在,我尝试使用AVAssetExportPresetPassthrough
导出媒体(仅供参考,100%的次数,所需的预设始终包含在列表中,但我也尝试了传递功能选项,但仍然无法正常工作
- I check the
isExportable
property ofAVAsset
- I then get an array of
exportPresets
compatible with theAVAsset
instance - I take the
AVAssetExportPreset1920x1080
, or, if not existing I try to export the media withAVAssetExportPresetPassthrough
(FYI, 100% of times, the preset I need is always contained in the list, but I tried also the passthrough option and it doesn't work anyway)
outputFileType
是AVFileTypeMPEG4
,我也尝试通过将.mp4
扩展名分配给该文件,但是没有任何使其工作.
我总是会收到此错误
The outputFileType
is AVFileTypeMPEG4
and I tried also by assigning the .mp4
extension to the file, but nothing makes it work.
I always receive this error
Error Domain = AVFoundationErrorDomain代码= -11838操作已停止" UserInfo = {NSUnderlyingError = 0x600000658c30 {Error Domain = NSOSStatusErrorDomain Code = -12109(null)"}, NSLocalizedFailureReason =此操作不受支持 媒体.,NSLocalizedDescription =操作已停止}
Error Domain=AVFoundationErrorDomain Code=-11838 "Operation Stopped" UserInfo={NSUnderlyingError=0x600000658c30 {Error Domain=NSOSStatusErrorDomain Code=-12109 "(null)"}, NSLocalizedFailureReason=The operation is not supported for this media., NSLocalizedDescription=Operation Stopped}
下面是我正在使用的代码
Below is the code I'm using
func _getDataFor(_ item: AVPlayerItem, completion: @escaping (Data?) -> ()) {
guard item.asset.isExportable else {
completion(nil)
return
}
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: item.asset)
var preset: String = AVAssetExportPresetPassthrough
if compatiblePresets.contains(AVAssetExportPreset1920x1080) { preset = AVAssetExportPreset1920x1080 }
guard
let exportSession = AVAssetExportSession(asset: item.asset, presetName: preset),
exportSession.supportedFileTypes.contains(AVFileTypeMPEG4) else {
completion(nil)
return
}
var tempFileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp_video_data.mp4", isDirectory: false)
tempFileUrl = URL(fileURLWithPath: tempFileUrl.path)
exportSession.outputURL = tempFileUrl
exportSession.outputFileType = AVFileTypeMPEG4
let startTime = CMTimeMake(0, 1)
let timeRange = CMTimeRangeMake(startTime, item.duration)
exportSession.timeRange = timeRange
exportSession.exportAsynchronously {
print("\(exportSession.error)")
let data = try? Data(contentsOf: tempFileUrl)
_ = try? FileManager.default.removeItem(at: tempFileUrl)
completion(data)
}
}
推荐答案
好像在AVMutableComposition
中转换AVAsset
实例一样可以解决问题.如果有人愿意,请告诉我.
Seems like converting the AVAsset
instance in a AVMutableComposition
did the trick. If, please, anyone knows the reason why this works let me know.
这是新的_getDataFor(_:completion:)
方法实现
func _getDataFor(_ item: AVPlayerItem, completion: @escaping (Data?) -> ()) {
guard item.asset.isExportable else {
completion(nil)
return
}
let composition = AVMutableComposition()
let compositionVideoTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
let compositionAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
let sourceVideoTrack = item.asset.tracks(withMediaType: AVMediaTypeVideo).first!
let sourceAudioTrack = item.asset.tracks(withMediaType: AVMediaTypeAudio).first!
do {
try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, item.duration), of: sourceVideoTrack, at: kCMTimeZero)
try compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, item.duration), of: sourceAudioTrack, at: kCMTimeZero)
} catch(_) {
completion(nil)
return
}
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: composition)
var preset: String = AVAssetExportPresetPassthrough
if compatiblePresets.contains(AVAssetExportPreset1920x1080) { preset = AVAssetExportPreset1920x1080 }
guard
let exportSession = AVAssetExportSession(asset: composition, presetName: preset),
exportSession.supportedFileTypes.contains(AVFileTypeMPEG4) else {
completion(nil)
return
}
var tempFileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp_video_data.mp4", isDirectory: false)
tempFileUrl = URL(fileURLWithPath: tempFileUrl.path)
exportSession.outputURL = tempFileUrl
exportSession.outputFileType = AVFileTypeMPEG4
let startTime = CMTimeMake(0, 1)
let timeRange = CMTimeRangeMake(startTime, item.duration)
exportSession.timeRange = timeRange
exportSession.exportAsynchronously {
print("\(tempFileUrl)")
print("\(exportSession.error)")
let data = try? Data(contentsOf: tempFileUrl)
_ = try? FileManager.default.removeItem(at: tempFileUrl)
completion(data)
}
}
这篇关于通过AVAssetExportSession导出mp4失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!