How to do Slow Motion video in IOS

前端 未结 6 1338
小蘑菇
小蘑菇 2020-11-28 21:14

I have to do \"slow motion\" in a video file along with audio, in-between some frames and need to store the ramped video as a new video.

6条回答
  •  情歌与酒
    2020-11-28 22:16

    Slower + Faster with or without audio track

    I have tried and able to Slower the asset.

    compositionVideoTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration) did the trick.

    I made a class which will help you to generate a slower video from AVAsset. + point is you can also make it faster and another + point is it will handle the audio too.

    Here is my custom class sample:

    import UIKit
    import AVFoundation
    
    enum SpeedoMode {
        case Slower
        case Faster
    }
    
    class VSVideoSpeeder: NSObject {
    
        /// Singleton instance of `VSVideoSpeeder`
        static var shared: VSVideoSpeeder = {
           return VSVideoSpeeder()
        }()
    
        /// Range is b/w 1x, 2x and 3x. Will not happen anything if scale is out of range. Exporter will be nil in case url is invalid or unable to make asset instance.
        func scaleAsset(fromURL url: URL,  by scale: Int64, withMode mode: SpeedoMode, completion: @escaping (_ exporter: AVAssetExportSession?) -> Void) {
    
            /// Check the valid scale
            if scale < 1 || scale > 3 {
                /// Can not proceed, Invalid range
                completion(nil)
                return
            }
    
            /// Asset
            let asset = AVAsset(url: url)
    
            /// Video Tracks
            let videoTracks = asset.tracks(withMediaType: AVMediaType.video)
            if videoTracks.count == 0 {
                /// Can not find any video track
                completion(nil)
                return
            }
    
            /// Get the scaled video duration
            let scaledVideoDuration = (mode == .Faster) ? CMTimeMake(asset.duration.value / scale, asset.duration.timescale) : CMTimeMake(asset.duration.value * scale, asset.duration.timescale)
            let timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
    
            /// Video track
            let videoTrack = videoTracks.first!
    
            let mixComposition = AVMutableComposition()
            let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
    
            /// Audio Tracks
            let audioTracks = asset.tracks(withMediaType: AVMediaType.audio)
            if audioTracks.count > 0 {
                /// Use audio if video contains the audio track
                let compositionAudioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
    
                /// Audio track
                let audioTrack = audioTracks.first!
                do {
                    try compositionAudioTrack?.insertTimeRange(timeRange, of: audioTrack, at: kCMTimeZero)
                    compositionAudioTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
                } catch _ {
                    /// Ignore audio error
                }
            }
    
            do {
                try compositionVideoTrack?.insertTimeRange(timeRange, of: videoTrack, at: kCMTimeZero)
                compositionVideoTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
    
                /// Keep original transformation
                compositionVideoTrack?.preferredTransform = videoTrack.preferredTransform
    
                /// Initialize Exporter now
                let outputFileURL = URL(fileURLWithPath: "/Users/thetiger/Desktop/scaledVideo.mov")
               /// Note:- Please use directory path if you are testing with device.
    
                if FileManager.default.fileExists(atPath: outputFileURL.absoluteString) {
                    try FileManager.default.removeItem(at: outputFileURL)
                }
    
                let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
                exporter?.outputURL = outputFileURL
                exporter?.outputFileType = AVFileType.mov
                exporter?.shouldOptimizeForNetworkUse = true
                exporter?.exportAsynchronously(completionHandler: {
                    completion(exporter)
                })
    
            } catch let error {
                print(error.localizedDescription)
                completion(nil)
                return
            }
        }
    
    }
    

    I took 1x, 2x and 3x as a valid scale. Class contains the proper validation and handling. Below is the sample of how to use this function.

    let url = Bundle.main.url(forResource: "1", withExtension: "mp4")!
    VSVideoSpeeder.shared.scaleAsset(fromURL: url, by: 3, withMode: SpeedoMode.Slower) { (exporter) in
         if let exporter = exporter {
             switch exporter.status {
                    case .failed: do {
                          print(exporter.error?.localizedDescription ?? "Error in exporting..")
                    }
                    case .completed: do {
                          print("Scaled video has been generated successfully!")
                    }
                    case .unknown: break
                    case .waiting: break
                    case .exporting: break
                    case .cancelled: break
               }
          }
          else {
               /// Error
               print("Exporter is not initialized.")
          }
    }
    

    This line will handle the audio scaling

    compositionAudioTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)

提交回复
热议问题