如何合并具有不同帧速率的多个视频?

时间:2016-10-19 15:09:29

标签: ios

我要求用户有多个剪辑,应用程序提供3个功能。用户可以进行慢动作,用户可以加快速度。更改速度后,用户可以合并它们以制作单个视频并保存到设备。

例如,

剪辑1,剪辑2和剪辑3以正常速度记录,然后以慢动作转换剪辑1,剪辑2处于正常速度,剪辑3以快速速度进行,然后当用户合并时,这三个剪辑将组合在一起并使其成为可能在一个剪辑中,用户可以在社交网络中分享。

使用AVFoundation录制视频或从图库中选择视频

func convertVideoWithSpeed(completion:()->()) {

    if RecordedSegment.segments.count > 0 {
        self.exportVideoWithMode(RecordedSegment.segments.first!,title: "clip_\(counter).mp4", completion: { [unowned self] (path) in
            RecordedSegment.segments.removeFirst()
            self.counter = self.counter + 1
            self.mergedVideArray.append("clip_\(self.counter).mp4")
            self.convertVideoWithSpeed(completion)
        })
    } else {
        var arr1 = [NSURL]()
        for track in self.mergedVideArray {
            let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
            var finalURL = documentsURL.URLByAppendingPathComponent(track)
            arr1.append(finalURL)
        }

        self.mergeVideos(self.mergedVideArray, completion: { 
            completion()
        })
    }
}

转换不同剪辑的视频帧速率

func exportVideoWithMode(segment:RecordedSegment,title:String,completion:(path:String)->()) {
    let mixComposition = AVMutableComposition()
    let videoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
    let audioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
    let startTimer = kCMTimeZero
    print(RecordedSegment.segments)
    var size = CGSizeZero
    let astTrack = AVAsset(URL: NSURL(string: segment.path!)!)
    size = astTrack.tracksWithMediaType(AVMediaTypeVideo)[0].naturalSize
    do {
        try videoTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: astTrack.tracksWithMediaType(AVMediaTypeVideo)[0] , atTime: startTimer)
        try audioTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: astTrack.tracksWithMediaType(AVMediaTypeAudio)[0] , atTime: startTimer)
    } catch _ {
        print("Failed to load first track")
    }

    let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let finalURL = documentsURL.URLByAppendingPathComponent(title)

    let instruction = AVMutableVideoCompositionInstruction()
    let layerInstruct = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
    instruction.layerInstructions = [layerInstruct]
    let videoComposition = AVMutableVideoComposition()
    videoComposition.instructions = [instruction]
    if segment.mode == .Slow {
        videoComposition.frameDuration = CMTimeMake(1, 15)
    } else if segment.mode == .Fast {
        videoComposition.frameDuration = CMTimeMake(1, 30)
    } else {
        videoComposition.frameDuration = CMTimeMake(1, 30)
    }
    videoComposition.renderSize = size
    videoComposition.renderScale = 1
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, astTrack.duration)

    guard let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
    exportSession.outputURL = finalURL
    exportSession.outputFileType = AVFileTypeQuickTimeMovie
    exportSession.shouldOptimizeForNetworkUse = true
    exportSession.videoComposition = videoComposition

    if NSFileManager.defaultManager().fileExistsAtPath(finalURL.path!) {
        do {
            try NSFileManager.defaultManager().removeItemAtURL(finalURL)
        } catch {

        }
    }

    // 6 - Perform the Export
    exportSession.exportAsynchronouslyWithCompletionHandler() {

        let error = exportSession.error?.code
        print(exportSession.error)
        if exportSession.status == .Cancelled {
            print("Export was cancelled")
            GlobalUtility.hideActivityIndi(self)
        } else if exportSession.status == .Completed {
            print("completed")
            let asset = AVAsset(URL: finalURL)
            let track = asset.tracksWithMediaType(AVMediaTypeVideo)[0]
            print("==============\(track.nominalFrameRate)")
            completion(path: finalURL.path!)
        } else if error == nil{
            completion(path: finalURL.path!)
        }else{
            if exportSession.status == .Cancelled {
                print("Export was cancelled")
                GlobalUtility.hideActivityIndi(self)
            }
            GlobalUtility.hideActivityIndi(self)
        }
    }
}

将它们合并到一个视频

func mergeVideos(mergePaths:[String],completion:()->()) {
    var count = 0
    let mixComposition = AVMutableComposition()
    let videoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
    let audioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
    var startTimer = kCMTimeZero
    print(RecordedSegment.segments)
    for track in mergePaths {
        let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
        var finalURL = documentsURL.URLByAppendingPathComponent(track)
        if NSFileManager.defaultManager().fileExistsAtPath(finalURL.path!) {
            let astTrack = AVAsset(URL: finalURL)
            let video = astTrack.tracksWithMediaType(AVMediaTypeVideo)
            let audio = astTrack.tracksWithMediaType(AVMediaTypeAudio)
            if audio.count > 0 && video.count > 0 {
                do {
                    try videoTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: video[0] , atTime: startTimer)
                    try audioTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: audio[0] , atTime: startTimer)
                    startTimer = (videoTrack.asset?.duration)!
                } catch _ {
                    print("Failed to load first track")
                }
            } else {
                print("file not exist")
            }
        } else {
            print("tracks not exist")
        }
    }
    //let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let finalURL = self.recordSession.outputUrl
    count = count + 1

    guard let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
    exportSession.outputURL = finalURL
    exportSession.outputFileType = AVFileTypeQuickTimeMovie
    exportSession.shouldOptimizeForNetworkUse = true

    if NSFileManager.defaultManager().fileExistsAtPath(self.recordSession.outputUrl.path!) {
        do {
            try NSFileManager.defaultManager().removeItemAtURL(self.recordSession.outputUrl)
        } catch {

        }
    }

    // 6 - Perform the Export
    exportSession.exportAsynchronouslyWithCompletionHandler() {

        let error = exportSession.error?.code
        print(exportSession.error)
        if exportSession.status == .Cancelled {
            print("Export was cancelled")
            GlobalUtility.hideActivityIndi(self)
        } else if exportSession.status == .Completed {
            print("completed")
            completion()
        } else if error == nil{
            completion()
        }else{
            if exportSession.status == .Cancelled {
                print("Export was cancelled")
                GlobalUtility.hideActivityIndi(self)
            }
            GlobalUtility.hideActivityIndi(self)
        }
    }
}

0 个答案:

没有答案