通过AVAssetExportSession导出mp4失败

时间:2017-01-09 09:04:37

标签: swift avfoundation avassetexportsession

我开始说我花了很多时间在文档,帖子和其他地方搜索,但我无法找到解决此问题的方法。

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

  • 我查看isExportable
  • AVAsset属性
  • 然后我得到一个与exportPresets实例
  • 兼容的AVAsset数组
  • 我选择AVAssetExportPreset1920x1080,或者,如果不存在,我尝试使用AVAssetExportPresetPassthrough导出媒体(仅供参考,100%,我需要的预设始终包含在列表中,但我也尝试过passthrough选项,但它无论如何都不起作用)

outputFileTypeAVFileTypeMPEG4,我也尝试将.mp4扩展名分配给文件,但没有任何内容可以使用。 我总是收到此错误

  

错误域= AVFoundationErrorDomain代码= -11838"操作已停止"   UserInfo = {NSUnderlyingError = 0x600000658c30 {错误   Domain = NSOSStatusErrorDomain Code = -12109"(null)"},   NSLocalizedFailureReason =不支持此操作   media。,NSLocalizedDescription = Operation Stopped}

以下是我使用

的代码
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)
    }
}

6 个答案:

答案 0 :(得分:14)

似乎转换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)
    }
}

答案 1 :(得分:2)

我遇到了同样的问题,因为我要在没有音频的视频中添加音轨。删除音轨,将其固定。

答案 2 :(得分:1)

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

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

符合AVAssetResourceLoaderDelegate协议。 这就是你需要做的一切。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

快捷键5:

import Foundation
import AVKit

func getDataFor(_ item: AVPlayerItem, completion: @escaping (Data?) -> ()) {
guard item.asset.isExportable 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))

let sourceVideoTrack = item.asset.tracks(withMediaType: .video).first!
let sourceAudioTrack = item.asset.tracks(withMediaType: .audio).first!
do {
    try compositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: item.duration), of: sourceVideoTrack, at: CMTime.zero)
    try compositionAudioTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: item.duration), of: sourceAudioTrack, at: CMTime.zero)
} 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(.mp4) 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 = .mp4
let startTime = CMTimeMake(value: 0, timescale: 1)
let timeRange = CMTimeRangeMake(start: startTime, duration: item.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)
}
}

答案 5 :(得分:0)

我解决了此问题,方法是从介质CompositionTrack中删除介质类型为.audio的{​​{1}}并清空segments

AVMutableComposition