Swift Merge AVasset-Videos array

后端 未结 3 1887
忘了有多久
忘了有多久 2021-02-04 20:18

I want to merge the AVAsset-arrayVideos into one single video and save it on camera roll. Raywenderlich.com has a great tutorial where two videos are merged into on

3条回答
  •  忘了有多久
    2021-02-04 20:42

    Swift 4

    Use like

    MeargeVide.mergeVideoArray(arrayVideos: arrayAsset) { (urlMeargeVide, error) in
     debugPrint("url",urlMeargeVide ?? "")
                    debugPrint("error",error ?? "")
    }
    

    Complete class with orientation and merge multiple clip in single.

    class MeargeVide {
    
       static func orientationFromTransform(_ transform: CGAffineTransform)
            -> (orientation: UIImageOrientation, isPortrait: Bool) {
                var assetOrientation = UIImageOrientation.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 = .left
                    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)
        }
    
        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 = UIScreen.main.bounds.width / assetTrack.naturalSize.width
                if assetInfo.isPortrait {
                    scaleToFitRatio = UIScreen.main.bounds.width / assetTrack.naturalSize.height
                    let scaleFactor = CGAffineTransform(scaleX: scaleToFitRatio, y: scaleToFitRatio)
                    instruction.setTransform(assetTrack.preferredTransform.concatenating(scaleFactor), at: kCMTimeZero)
                } 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: kCMTimeZero)
                }
    
                return instruction
        }
    
        class func mergeVideoArray(arrayVideos:[AVAsset], callBack:@escaping (_ urlGet:URL?,_ errorGet:Error?) -> Void){
    
            var atTimeM: CMTime = CMTimeMake(0, 0)
            var lastAsset: AVAsset!
            var layerInstructionsArray = [AVVideoCompositionLayerInstruction]()
            var completeTrackDuration: CMTime = CMTimeMake(0, 1)
            var videoSize: CGSize = CGSize(width: 0.0, height: 0.0)
            var totalTime : CMTime = CMTimeMake(0, 0)
    
            let mixComposition = AVMutableComposition.init()
            for videoAsset in arrayVideos{
    
                let videoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
                do {
                    if videoAsset == arrayVideos.first {
                        atTimeM = kCMTimeZero
                    } else {
                        atTimeM = totalTime // <-- Use the total time for all the videos seen so far.
                    }
                    try videoTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration),
                                                    of: videoAsset.tracks(withMediaType: AVMediaType.video)[0],
                                                    at: completeTrackDuration)
                    videoSize = (videoTrack?.naturalSize)!
    
    
    
                } catch let error as NSError {
                    print("error: \(error)")
                }
    
                totalTime = CMTimeAdd(totalTime, videoAsset.duration)
    
    
    
                completeTrackDuration = CMTimeAdd(completeTrackDuration, videoAsset.duration)
    
                let firstInstruction = self.videoCompositionInstruction(videoTrack!, asset: videoAsset)
                firstInstruction.setOpacity(0.0, at: videoAsset.duration)
    
                layerInstructionsArray.append(firstInstruction)
                lastAsset = videoAsset
            }
    
    
            let mainInstruction = AVMutableVideoCompositionInstruction()
            mainInstruction.layerInstructions = layerInstructionsArray
            mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, completeTrackDuration)
    
            let mainComposition = AVMutableVideoComposition()
            mainComposition.instructions = [mainInstruction]
            mainComposition.frameDuration = CMTimeMake(1, 30)
            mainComposition.renderSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
    
            let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            let dateFormatter = DateFormatter()
            dateFormatter.dateStyle = .long
            dateFormatter.timeStyle = .short
            let date = dateFormatter.string(from: NSDate() as Date)
            let savePath = (documentDirectory as NSString).appendingPathComponent("mergeVideo-\(date).mov")
            let url = NSURL(fileURLWithPath: savePath)
    
            let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
            exporter!.outputURL = url as URL
            exporter!.outputFileType = AVFileType.mp4
            exporter!.shouldOptimizeForNetworkUse = true
            exporter!.videoComposition = mainComposition
            exporter!.exportAsynchronously {
                DispatchQueue.main.async {
                    callBack(exporter?.outputURL, exporter?.error)
                }
    
            }
        } 
    }
    

提交回复
热议问题