通过AVAssetExportSession导出mp4失败



我开始说我花了很多时间搜索文档、这里和其他地方的帖子,但我找不出这个问题的解决方案。

我使用AVAssetExportSession导出存储在AVAsset实例中的.mp4文件。我所做的是:

  • 我检查AVAssetisExportable属性
  • 然后我得到一个与AVAsset实例兼容的exportPresets数组
  • 我使用AVAssetExportPreset1920x1080,或者,如果不存在,我尝试使用AVAssetExportPresetPassthrough导出介质(仅供参考,100%的时候,我需要的预设总是包含在列表中,但我也尝试了passthrough选项,但它无论如何都不起作用)

outputFileTypeAVFileTypeMPEG4,我也尝试过为文件分配.mp4扩展名,但没有任何东西能使它工作。我总是收到这个错误

Error Domain=AVFoundationErrorDomain Code=-11838"操作已停止"UserInfo={NSUnderlyingError=0x600000658c30{错误域=NSOSStatusErrorDomain Code=-12109"(null)"},NSLocalizedFailureReason=此操作不受支持媒体NSLocalizedDescription=操作已停止}

下面是我正在使用的代码

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实例就成功了。如果有人知道为什么这样做,请告诉我。

这是新的_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)
}
}

Swift 5:

import Foundation
import AVKit
func getDataFor(_ asset: AVAsset, completion: @escaping (Data?) -> ()) {

guard asset.isExportable,
let sourceVideoTrack = asset.tracks(withMediaType: .video).first,
let sourceAudioTrack = asset.tracks(withMediaType: .audio).first else {
completion(nil)
return
}

let composition = AVMutableComposition()
let compositionVideoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
let compositionAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))

do {
try compositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: sourceVideoTrack, at: .zero)
try compositionAudioTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: sourceAudioTrack, at: .zero)
} catch {
completion(nil)
return
}

let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: composition)
var preset = AVAssetExportPresetPassthrough
let preferredPreset = AVAssetExportPreset1920x1080
if compatiblePresets.contains(preferredPreset) {
preset = preferredPreset
}

let fileType: AVFileType = .mp4
guard let exportSession = AVAssetExportSession(asset: composition, presetName: preset),
exportSession.supportedFileTypes.contains(fileType) else {
completion(nil)
return
}

let tempFileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("temp_video_data")

exportSession.outputURL = tempFileUrl
exportSession.outputFileType = fileType
let startTime = CMTimeMake(value: 0, timescale: 1)
let timeRange = CMTimeRangeMake(start: startTime, duration: asset.duration)
exportSession.timeRange = timeRange

exportSession.exportAsynchronously {
print(tempFileUrl)
print(String(describing: exportSession.error))
let data = try? Data(contentsOf: tempFileUrl)
try? FileManager.default.removeItem(at: tempFileUrl)
completion(data)
}
}

检查是否正确设置了AVURLAsset的委托属性。

[self.playerAsset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];

并符合AVAssetResourceLoaderDelegate协议。这就是你所需要做的。

我遇到了同样的问题,因为我在没有音频的视频中添加了音轨。删除音轨修复了它。

我遇到了这个问题,因为Microphone权限被关闭/拒绝。一旦我把它打开,这个错误就消失了。

我通过从AVMutableComposition中删除介质类型为.audio和空segmentsCompositionTrack解决了这个问题

if let audioTrack = exportComposition.tracks(withMediaType: .audio).first,
audioTrack.segments.isEmpty {
exportComposition.removeTrack(audioTrack)
}

我遇到了同样的错误,当我尝试使用AVAssetExportPresetPassthrough进行ExportSession时总是失败,在我的情况下,我不能使用其他预设,因为我必须具有与原始视频相同的分辨率

我修复了

let asset = AVAsset(url: originUrl)
let videoTrack = asset.tracks( withMediaType: .video ).first! as AVAssetTrack
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(
width: videoTrack.naturalSize.width, 
height: videoTrack.naturalSize.height
)
videoComposition.frameDuration = CMTime(
value: 1, 
timescale: videoTrack.naturalTimeScale
)

let transformer = AVMutableVideoCompositionLayerInstruction(
assetTrack: videoTrack 
)
transformer.setOpacity(1.0, at: CMTime.zero)

let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = timeRange
instruction.layerInstructions = [transformer]
videoComposition.instructions = [instruction]
guard let exportSession = AVAssetExportSession(
asset: asset, 
presetName: AVAssetExportPresetMediumQuality
) else {
return handleFailure(error: .mediaSavingError, completion: completion)
}

exportSession.videoComposition = videoComposition
exportSession.outputURL = outputUrl
exportSession.outputFileType = .mp4
exportSession.timeRange = timeRange
exportSession.exportAsynchronously { [weak self] in 
"your code"
}

叉子对我来说很棒,它保存的分辨率与之前的视频相同

最新更新