Created
November 6, 2019 08:44
-
-
Save trilliwon/ed75d38ac9c0db62f3fe04ffd1823db8 to your computer and use it in GitHub Desktop.
VideoTrimmer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AVFoundation | |
class VideoTrimmer { | |
typealias TrimCompletion = (Error?) -> () | |
typealias TrimPoints = [(CMTime, CMTime)] | |
func verifyPresetForAsset(preset: String, asset: AVAsset) -> Bool { | |
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: asset) | |
let filteredPresets = compatiblePresets.filter { $0 == preset } | |
return filteredPresets.count > 0 || preset == AVAssetExportPresetPassthrough | |
} | |
func removeFileAtURLIfExists(url: URL) { | |
let fileManager = FileManager.default | |
guard fileManager.fileExists(atPath: url.path) else { return } | |
do { | |
try fileManager.removeItem(at: url) | |
} | |
catch let error { | |
print("TrimVideo - Couldn't remove existing destination file: \(String(describing: error))") | |
} | |
} | |
func trimVideo(sourceURL: URL, destinationURL: URL, trimPoints: TrimPoints, completion: TrimCompletion?) { | |
guard sourceURL.isFileURL else { return } | |
guard destinationURL.isFileURL else { return } | |
let options = [ | |
AVURLAssetPreferPreciseDurationAndTimingKey: true | |
] | |
let asset = AVURLAsset(url: sourceURL, options: options) | |
let preferredPreset = AVAssetExportPresetPassthrough | |
if verifyPresetForAsset(preset: preferredPreset, asset: asset) { | |
let composition = AVMutableComposition() | |
let videoCompTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: CMPersistentTrackID()) | |
let audioCompTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: CMPersistentTrackID()) | |
guard let assetVideoTrack: AVAssetTrack = asset.tracks(withMediaType: .video).first else { return } | |
guard let assetAudioTrack: AVAssetTrack = asset.tracks(withMediaType: .audio).first else { return } | |
videoCompTrack!.preferredTransform = assetVideoTrack.preferredTransform | |
var accumulatedTime = CMTime.zero | |
for (startTimeForCurrentSlice, endTimeForCurrentSlice) in trimPoints { | |
let durationOfCurrentSlice = CMTimeSubtract(endTimeForCurrentSlice, startTimeForCurrentSlice) | |
let timeRangeForCurrentSlice = CMTimeRangeMake(start: startTimeForCurrentSlice, duration: durationOfCurrentSlice) | |
do { | |
try videoCompTrack!.insertTimeRange(timeRangeForCurrentSlice, of: assetVideoTrack, at: accumulatedTime) | |
try audioCompTrack!.insertTimeRange(timeRangeForCurrentSlice, of: assetAudioTrack, at: accumulatedTime) | |
accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice) | |
} | |
catch let compError { | |
print("TrimVideo: error during composition: \(compError)") | |
completion?(compError) | |
} | |
} | |
guard let exportSession = AVAssetExportSession(asset: composition, presetName: preferredPreset) else { return } | |
exportSession.outputURL = destinationURL as URL | |
exportSession.outputFileType = AVFileType.m4v | |
exportSession.shouldOptimizeForNetworkUse = true | |
removeFileAtURLIfExists(url: destinationURL as URL) | |
exportSession.exportAsynchronously { | |
completion?(exportSession.error) | |
} | |
} | |
else { | |
print("TrimVideo - Could not find a suitable export preset for the input video") | |
let error = NSError(domain: "com.bighug.ios", code: -1, userInfo: nil) | |
completion?(error) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment