簡體   English   中英

AVAssetExportSession 不適用於設備,但適用於模擬器(AVFoundationErrorDomain 代碼 = -11800,未知錯誤代碼 -12780)

[英]AVAssetExportSession not working on devices, but working on simulator (AVFoundationErrorDomain Code = -11800, Unknown Error code -12780)

編輯:為了讓任何有興趣檢查這個問題的人更容易,我在 這個 github 存儲庫中添加了一個演示項目

問題

我見過幾個有相同錯誤的問題,但在那里找到的解決方案都沒有幫助我。 想我會試試我的運氣。

我正在嘗試按原樣導出視頻,主要是為了了解 AVFoundation 和 AVAssetExportSession。 我的導出在模擬器上運行良好,但在我嘗試過的任何 iOS 設備(即 iPhone X 和運行 iOS 12 的 iPhone XR)上都不起作用。 我主要按照此鏈接上的 Ray Wenderleich 教程執行視頻導出: https ://www.raywenderlich.com/2734-avfoundation-tutorial-adding-overlays-and-animations-to-videos

將不勝感激有關該主題的任何幫助。 我的代碼如下:

檢索我添加到名為 Demo.mp4 的 App Bundle 的視頻的 URL:

@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 函數:

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

    if FileManager.default.fileExists(atPath: myPathDocs) {
        try? FileManager.default.removeItem(atPath: myPathDocs)
    }

    return url
}

注意:createPath() 只是在目錄中創建一個有效的路徑來保存導出的視頻。如果在導出之前該路徑中存在文件,它將被刪除。

問題是您將字符串附加到另一個字符串,導致文件路徑錯誤,例如file:///var/mobile/Containers/Data/Application/<stripped>/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
}

您必須使用默認方法:appendingPathComponent()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM