AVAssetExportSession在设备上不起作用,但在模拟器上起作用(AVFoundationErrorDomain代码= -11800,未知错误代码-12780)

时间:2019-07-24 14:16:29

标签: ios avfoundation avassetexportsession

我看到了几个具有相同错误的问题,但是那里没有找到解决方案对我有帮助。想通了,我会试试运气。

我正尝试按原样导出视频,主要是为了了解AVFoundation和AVAssetExportSession。我的导出在模拟器上可以很好地工作,但是在我尝试过的任何iOS设备上均不起作用(即分别运行iOS 12的iPhone X和iPhone XR)。我主要遵循在此链接上找到的Ray Wenderleich教程来执行视频导出:https://www.raywenderlich.com/2734-avfoundation-tutorial-adding-overlays-and-animations-to-videos

将感谢您对该主题的任何帮助。我的代码如下:

检索我添加到App Bundle中的视频的URL,该视频名为Demo.mp4:

@objc func export() {
    let urlString = Bundle.main.path(forResource: "Demo", ofType: ".mp4")!
    let url = URL(fileURLWithPath: urlString)
    ExportManager.shared.exportWithAVFoundation(url:url) { (outputUrl, errorString) in
        if let outputUrl = outputUrl {
            self.playVideo(url: outputUrl)
        } else if let errorString = errorString {
            print("ERROR: \(errorString)")
        }
    }
}

我在ExportManager中的导出功能如下(很抱歉,很长一段时间)

func exportWithAVFoundation(url: URL, completion: @escaping (_ outputUrl: URL?, _ errorString: String?) -> ()) {
    let asset = AVAsset(url: url)
    print("URL IS \(url)")
    guard let avAssetTrack = asset.tracks(withMediaType: .video).first else {
        completion(nil, "Couldn't Create Asset Track")
        return
    }

    let mutableComposition = AVMutableComposition()
    guard let videoTrack = mutableComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else { return }
    try? videoTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: avAssetTrack, at: .zero)
    videoTrack.preferredTransform = avAssetTrack.preferredTransform

    if let audioAssetTrack = asset.tracks(withMediaType: .audio).first {
        let audioTrack = mutableComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
        try? audioTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: audioAssetTrack, at: .zero)
    }

    let mainInstruction = AVMutableVideoCompositionInstruction()
    mainInstruction.timeRange = CMTimeRange(start: .zero, duration: asset.duration)

    let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)

    // Fix video orientation
    var videoAssetOrientation = UIImage.Orientation.up
    var isVideoAssetPortrait = false
    let videoTransform = avAssetTrack.preferredTransform

    switch (videoTransform.a, videoTransform.b, videoTransform.c, videoTransform.c) {
    case (0, 1.0, -1.0, 0):
        videoAssetOrientation = .right
        isVideoAssetPortrait = true
    case(0, -1.0, 1.0, 0):
        videoAssetOrientation = .left
        isVideoAssetPortrait = true
    case(1.0, 0, 0, 1.0):
        videoAssetOrientation = .up
    case(-1.0, 0, 0, -1.0):
        videoAssetOrientation = .down
    default:
        break
    }

    var naturalSize = avAssetTrack.naturalSize
    switch (videoAssetOrientation, isVideoAssetPortrait) {
    case (.right, true):
        naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
    case (.left, true):
        naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
    case (.leftMirrored, true):
        naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
    case (.rightMirrored, true):
        naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
    default:
        break
    }

    videoLayerInstruction.setTransform(avAssetTrack.preferredTransform, at: .zero)
    videoLayerInstruction.setOpacity(0, at: asset.duration)

    mainInstruction.layerInstructions = [videoLayerInstruction]

    let mainCompositionInstruction = AVMutableVideoComposition()

    mainCompositionInstruction.renderSize = naturalSize
    mainCompositionInstruction.instructions = [mainInstruction]
    mainCompositionInstruction.frameDuration = CMTimeMake(value: 1, timescale: 30);

    let documentsDirectoryURL = createPath()

    guard let exporter = AVAssetExportSession(asset: mutableComposition, presetName: AVAssetExportPresetHighestQuality) else {
        print("Couldnt create AVAssetExportSession")

        completion(nil, "Couldn't Create AVAssetExportSession")
        return
    }

    exporter.outputURL = documentsDirectoryURL
    exporter.outputFileType = .mov
    exporter.shouldOptimizeForNetworkUse = true
    exporter.videoComposition = mainCompositionInstruction

    exporter.exportAsynchronously {
        if let error = exporter.error {
            print(error)
            completion(nil, error.localizedDescription)
            return
        }

        completion(exporter.outputURL, nil)
        print("Finished Exporting")
    }
}

我尝试做的一些事情是将AudioTrack添加到合成中(我以前没有包括过)。并没有帮助它在实际设备上运行,但是至少我导出的视频现在有音频。

我还尝试将“预设质量”更改为“通过”,而不是“最高质量”,正如我从其他主题中读到的那样,这可能有所帮助,但无济于事。

注意: createPath()只是在目录中创建一个有效路径来保存导出的视频。如果在导出之前该路径上存在文件,则会将其删除。

2 个答案:

答案 0 :(得分:1)

问题是您将字符串附加到另一个字符串,导致该文件的路径错误,例如 file:/// var / mobile / Containers / Data / Application / / DocumentsFinalVideo。 mov

您应该改用 appendingPathComponent()

func createPath() -> URL {
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let documentDirectory = URL(fileURLWithPath: paths.first!)
    let url = documentDirectory.appendingPathComponent("FinalVideo.mov")

    if FileManager.default.fileExists(atPath: url.path) {
        try? FileManager.default.removeItem(at: url)
    }

    return url
}

答案 1 :(得分:0)

您必须使用默认方法:     appendingPathComponent()