AVExportSession 导出视频超慢

AVExportSession exporting video super slow

我只是想提高导出合并视频的速度。

这是代码://根据我在网上和 SO 上的广泛研究,我几乎归结为预设的 PassThrough 使其超快,但是正如我在代码的评论中写的那样,我的合并代码似乎不适用于该导出预设:/

            static func videoCompositionInstruction(_ track: AVCompositionTrack, asset: AVAsset)
            -> AVMutableVideoCompositionLayerInstruction {
                let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
                let assetTrack = asset.tracks(withMediaType: .video)[0]

                let transform = assetTrack.preferredTransform
                let assetInfo = orientationFromTransform(transform)

                var scaleToFitRatio = 1080 / assetTrack.naturalSize.width
                if assetInfo.isPortrait {
                    scaleToFitRatio = 1080 / assetTrack.naturalSize.height
                    let scaleFactor = CGAffineTransform(scaleX: scaleToFitRatio, y: scaleToFitRatio)
                    var finalTransform = assetTrack.preferredTransform.concatenating(scaleFactor)
                    //was needed in my case (if video not taking entire screen and leaving some parts black - don't know when actually needed so you'll have to try and see when it's needed)
                    if assetInfo.orientation == .rightMirrored || assetInfo.orientation == .leftMirrored {
                        finalTransform = finalTransform.translatedBy(x: -transform.ty, y: 0)
                    }
                    instruction.setTransform(finalTransform, at: CMTime.zero)
                } else {
                    let scaleFactor = CGAffineTransform(scaleX: scaleToFitRatio, y: scaleToFitRatio)
                    var concat = assetTrack.preferredTransform.concatenating(scaleFactor)
                        .concatenating(CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.width / 2))
                    if assetInfo.orientation == .down {
                        let fixUpsideDown = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
                        let windowBounds = UIScreen.main.bounds
                        let yFix = assetTrack.naturalSize.height + windowBounds.height
                        let centerFix = CGAffineTransform(translationX: assetTrack.naturalSize.width, y: yFix)
                        concat = fixUpsideDown.concatenating(centerFix).concatenating(scaleFactor)
                    }
                    instruction.setTransform(concat, at: CMTime.zero)
                }

                return instruction
        }

        static func orientationFromTransform(_ transform: CGAffineTransform)
            -> (orientation: UIImage.Orientation, isPortrait: Bool) {
                var assetOrientation = UIImage.Orientation.up
                var isPortrait = false
                if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
                    assetOrientation = .right
                    isPortrait = true
                } else if transform.a == 0 && transform.b == 1.0 && transform.c == 1.0 && transform.d == 0 {
                    assetOrientation = .rightMirrored
                    isPortrait = true
                } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
                    assetOrientation = .left
                    isPortrait = true
                } else if transform.a == 0 && transform.b == -1.0 && transform.c == -1.0 && transform.d == 0 {
                    assetOrientation = .leftMirrored
                    isPortrait = true
                } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {
                    assetOrientation = .up
                } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {
                    assetOrientation = .down
                }
                return (assetOrientation, isPortrait)
        }

    func mergeVideosTestSQ(arrayVideos:[AVAsset], completion:@escaping (URL?, Error?) -> ()) {
            
            let mixComposition = AVMutableComposition()

            
            var instructions: [AVMutableVideoCompositionLayerInstruction] = []
            var insertTime = CMTime(seconds: 0, preferredTimescale: 1)

            /// for each URL add the video and audio tracks and their duration to the composition
            for sourceAsset in arrayVideos {
                    
                let frameRange = CMTimeRange(start: CMTime(seconds: 0, preferredTimescale: 1), duration: sourceAsset.duration)

                guard
                    let nthVideoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)),
                    let nthAudioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)), //0 used to be kCMPersistentTrackID_Invalid
                    let assetVideoTrack = sourceAsset.tracks(withMediaType: .video).first
                else {
                    print("didnt work")
                    return
                }

                var assetAudioTrack: AVAssetTrack?
                assetAudioTrack = sourceAsset.tracks(withMediaType: .audio).first
                print(assetAudioTrack, ",-- assetAudioTrack???", assetAudioTrack?.asset, "<-- hes", sourceAsset)
                
                do {
                    
                    try nthVideoTrack.insertTimeRange(frameRange, of: assetVideoTrack, at: insertTime)
                    try nthAudioTrack.insertTimeRange(frameRange, of: assetAudioTrack!, at: insertTime)
    
                    //instructions:
                    let nthInstruction = MainCamVC.videoCompositionInstruction(nthVideoTrack, asset: sourceAsset)
                    nthInstruction.setOpacity(0.0, at: CMTimeAdd(insertTime, sourceAsset.duration)) //sourceasset.duration

                    instructions.append(nthInstruction)
                    insertTime = insertTime + sourceAsset.duration //sourceAsset.duration

                    
                    
                } catch {
                    DispatchQueue.main.async {
                        print("didnt wor2k")
                    }
                }

            }
        

            let mainInstruction = AVMutableVideoCompositionInstruction()
            mainInstruction.timeRange = CMTimeRange(start: CMTime(seconds: 0, preferredTimescale: 1), duration: insertTime)

            mainInstruction.layerInstructions = instructions

            let mainComposition = AVMutableVideoComposition()
            mainComposition.instructions = [mainInstruction]
            mainComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
            mainComposition.renderSize = CGSize(width: 1080, height: 1920)

            let outputFileURL = URL(fileURLWithPath: NSTemporaryDirectory() + "merge.mp4")

            //below to clear the video form docuent folder for new vid...
            let fileManager = FileManager()
            try? fileManager.removeItem(at: outputFileURL)
            
        
            /// try to start an export session and set the path and file type
            if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) { //DOES NOT WORK WITH AVAssetExportPresetPassthrough
                exportSession.outputFileType = .mov
                exportSession.outputURL = outputFileURL
                exportSession.videoComposition = mainComposition
                exportSession.shouldOptimizeForNetworkUse = true

                /// try to export the file and handle the status cases
                exportSession.exportAsynchronously {
                    if let url = exportSession.outputURL{
                      completion(url, nil)
                  }
                    if let error = exportSession.error {
                      completion(nil, error)
                  }
                }
                
            }

        }

请注意,我有说明以保持正确的方向。

感谢您的帮助!我只是需要它更快,它大约需要 videoDuration/2 秒才能及时导出...

在我的项目中实施您的代码后,您处理 renderSize 的方式以及视频的分辨率似乎是导致导出速度变慢的原因。最重要的是,也许使用较低的质量预设可能会使它更高。

特别注意这部分:

    let mainInstruction = AVMutableVideoCompositionInstruction()
    mainInstruction.timeRange = CMTimeRange(start: CMTime(seconds: 0, preferredTimescale: 1), duration: insertTime)

    mainInstruction.layerInstructions = instructions

    let mainComposition = AVMutableVideoComposition()
    mainComposition.instructions = [mainInstruction]
    mainComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
    mainComposition.renderSize = CGSize(width: 1080, height: 1920)

将 renderSize 更改为 videoCompositions.size(可能是您的项目的不同名称)就可以了。

然后在导出的地方,我建议修改这部分:

    /// try to start an export session and set the path and file type
    if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) { //DOES NOT WORK WITH AVAssetExportPresetPassthrough
        exportSession.outputFileType = .mov
        exportSession.outputURL = outputFileURL
        exportSession.videoComposition = mainComposition
        exportSession.shouldOptimizeForNetworkUse = true

        /// try to export the file and handle the status cases
        exportSession.exportAsynchronously {
            if let url = exportSession.outputURL{
              completion(url, nil)
          }
            if let error = exportSession.error {
              completion(nil, error)
          }
        }
        
    }

之前说过,把presetQuality调低一档左右。它将大大提高速度!一些转换也被复制了,需要注意的地方!