Skip to content

Instantly share code, notes, and snippets.

@mspvirajpatel
Last active January 22, 2019 11:49
Show Gist options
  • Save mspvirajpatel/5c22da870e37f3d339e78dbd42336c62 to your computer and use it in GitHub Desktop.
Save mspvirajpatel/5c22da870e37f3d339e78dbd42336c62 to your computer and use it in GitHub Desktop.
// Media is a video
let videoUrl = "Your URL FILE" as! NSURL
let asset = AVAsset(url: videoUrl as URL)
let first: CMTimeRange = CMTimeRange.init(start: kCMTimeZero, duration: CMTime.init(seconds: 2, preferredTimescale: asset.duration.timescale))
let firstScael: CMTime = CMTimeMakeWithSeconds((Float64(2) * Float64(5)), asset.duration.timescale)
let second: CMTimeRange = CMTimeRange.init(start: CMTime.init(seconds: 5, preferredTimescale: asset.duration.timescale), duration: CMTime.init(seconds: 5, preferredTimescale: asset.duration.timescale))
let secondScael: CMTime = CMTimeMakeWithSeconds((Float64(5) / Float64(5.0)), asset.duration.timescale)
let thir: CMTimeRange = CMTimeRange.init(start: CMTime.init(seconds: 11, preferredTimescale: asset.duration.timescale), duration: CMTime.init(seconds: 2, preferredTimescale: asset.duration.timescale))
let thirScael: CMTime = CMTimeMakeWithSeconds((Float64(2) * Float64(2.0)), asset.duration.timescale)
let url = videoUrl
VSVideoSpeeder.shared.scaleAsset(fromURL: url as URL, by: 2, withMode: .Faster, withCMTime: [firstScael, secondScael, thirScael], withRange: [first, second, thir]) { (exporter) in
if let exporter = exporter {
switch exporter.status {
case .failed: do {
print(exporter.error?.localizedDescription ?? "Error in exporting..")
}
case .completed: do {
self.playAVPlayer(url: exporter.outputURL! as NSURL)
print("Scaled video has been generated successfully!")
}
case .unknown: break
case .waiting: break
case .exporting: break
case .cancelled: break
}
}
else {
print("Exporter is not initialized.")
}
}
//
// SlowFast.swift
//
// Created by Viraj Patel on 11/12/18.
//
import Foundation
import UIKit
import AVFoundation
enum SpeedoMode {
case Slower
case Faster
}
class VSVideoSpeeder: NSObject {
static var shared: VSVideoSpeeder = {
return VSVideoSpeeder()
}()
func margeVideos(compositionTrack: AVMutableCompositionTrack, withCMTime timeScale: [CMTime], withRange range: [CMTimeRange]) {
var temptimeRange2StartTime = range[0].end
var lastTimeRangeForStart = kCMTimeRangeZero
for (index, newRange) in range.enumerated() {
if index == 0 {
compositionTrack.scaleTimeRange(newRange, toDuration: timeScale[index])
}
else {
var timeRange2 = newRange
let timeRange = range[index - 1]
var indexItem = index
if index == 1 {
indexItem = 0
} else if index == 2 {
indexItem = 2
}
temptimeRange2StartTime = timeRange.end
let timeRangerrr = (lastTimeRangeForStart.start.seconds + (Double(compositionTrack.segments[indexItem].timeMapping.target.duration.value) / Double(compositionTrack.segments[indexItem].timeMapping.target.duration.timescale))) + Double(timeRange2.start.seconds - temptimeRange2StartTime.seconds)
print(timeRangerrr)
let duration = timeRange2.duration
timeRange2.start = CMTime.init(seconds: timeRangerrr, preferredTimescale: timeRange2.start.timescale)
timeRange2.duration = duration
compositionTrack.scaleTimeRange(timeRange2, toDuration: timeScale[index])
lastTimeRangeForStart = timeRange2
}
}
}
/// 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: Float, withMode mode: SpeedoMode, withCMTime timeScale: [CMTime], withRange range: [CMTimeRange], completion: @escaping (_ exporter: AVAssetExportSession?) -> Void) {
/// Asset
let asset = AVAsset(url: url)
/// Video Tracks
let videoTracks = asset.tracks(withMediaType: AVMediaTypeVideo)
if videoTracks.count == 0 {
/// Can not find any video track
completion(nil)
return
}
let fullTimeRange = CMTimeRangeMake(kCMTimeZero, CMTime.init(seconds: Double(asset.duration.seconds), preferredTimescale: asset.duration.timescale))
/// Video track
let videoTrack = videoTracks.first!
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
/// Audio Tracks
let audioTracks = asset.tracks(withMediaType: AVMediaTypeAudio)
if audioTracks.count > 0 {
/// Use audio if video contains the audio track
let compositionAudioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)
/// Audio track
let audioTrack = audioTracks.first!
do {
try compositionAudioTrack.insertTimeRange(fullTimeRange, of: audioTrack, at: kCMTimeZero)
margeVideos(compositionTrack: compositionAudioTrack, withCMTime: timeScale, withRange: range)
} catch _ {
/// Ignore audio error
}
}
do {
// videoTracks
try compositionVideoTrack.insertTimeRange(fullTimeRange, of: videoTrack, at: kCMTimeZero)
margeVideos(compositionTrack: compositionVideoTrack, withCMTime: timeScale, withRange: range)
print(compositionVideoTrack.segments)
print(mixComposition.duration)
/// Keep original transformation
compositionVideoTrack.preferredTransform = videoTrack.preferredTransform
/// Initialize Exporter now
// let outputFileURL = URL(fileURLWithPath: "/Users/viraj.patel/Desktop/scaledVideo222.mp4")
/// Note:- Please use directory path if you are testing with device.
let df = DateFormatter()
df.locale = NSLocale(localeIdentifier: "en_US") as Locale
df.dateFormat = "yyyyMMddHHmmssSSS"
var formate = df.string(from: Date())
formate = formate + ".mp4"
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let outputFileURL = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("\(formate)")
print(outputFileURL)
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 = AVFileTypeMPEG4
exporter?.shouldOptimizeForNetworkUse = true
exporter?.exportAsynchronously(completionHandler: {
completion(exporter)
})
} catch let error {
print(error.localizedDescription)
completion(nil)
return
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment