通过AVAssetExportSession导出mp4失败

问题描述 投票:6回答:3

[我开始说我花了很多时间搜索文档,在此处和其他地方发布的内容,但是我无法找到解决该问题的方法。

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

  • 我检查isExportableAVAsset属性
  • 然后我得到一个与exportPresets实例兼容的AVAsset数组
  • 我使用AVAssetExportPreset1920x1080,或者,如果不存在,我尝试使用AVAssetExportPresetPassthrough导出媒体(仅供参考,100%的次数,我需要的预设始终包含在列表中,但是我也尝试了直通选项而且还是不行)

outputFileTypeAVFileTypeMPEG4,我也尝试通过将.mp4扩展名分配给该文件,但没有任何使其起作用。我总是收到此错误

错误域= AVFoundationErrorDomain代码= -11838“操作已停止”UserInfo = {NSUnderlyingError = 0x600000658c30 {ErrorDomain = 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)
    }
}
swift avfoundation avassetexportsession
3个回答
17
投票

好像是在AVAsset中转换AVMutableComposition实例一样有效。如果有任何人知道,请告诉我。

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

2
投票

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

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

并且符合AVAssetResourceLoaderDelegate协议。这就是您需要做的。


0
投票

我遇到了这个问题,因为Microphone权限已关闭/被拒绝。一旦设置好,此错误就消失了。

© www.soinside.com 2019 - 2024. All rights reserved.