-
-
Save budidino/ffefdbd1a8a33607c54d495b6120850e to your computer and use it in GitHub Desktop.
Trim video using AVFoundation in Swift
This file contains 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
// | |
// TrimVideo.swift | |
// VideoLab | |
// | |
// Created by Adam Jensen on 3/28/15. | |
// Copyright (c) 2015 Adam Jensen. All rights reserved. | |
// | |
import AVFoundation | |
import Foundation | |
typealias TrimCompletion = (NSError?) -> () | |
typealias TrimPoints = [(CMTime, CMTime)] | |
func verifyPresetForAsset(preset: String, asset: AVAsset) -> Bool { | |
let compatiblePresets = AVAssetExportSession.exportPresetsCompatibleWithAsset(asset) as! [String] | |
let filteredPresets = compatiblePresets.filter { $0 == preset } | |
return filteredPresets.count > 0 || preset == AVAssetExportPresetPassthrough | |
} | |
func removeFileAtURLIfExists(url: NSURL) { | |
if let filePath = url.path { | |
let fileManager = NSFileManager.defaultManager() | |
if fileManager.fileExistsAtPath(filePath) { | |
var error: NSError? | |
if !fileManager.removeItemAtPath(filePath, error: &error) { | |
NSLog("Couldn't remove existing destination file: \(error)") | |
} | |
} | |
} | |
} | |
func trimVideo(sourceURL: NSURL, destinationURL: NSURL, trimPoints: TrimPoints, completion: TrimCompletion?) { | |
assert(sourceURL.fileURL) | |
assert(destinationURL.fileURL) | |
let options = [ AVURLAssetPreferPreciseDurationAndTimingKey: true ] | |
let asset = AVURLAsset(URL: sourceURL, options: options) | |
let preferredPreset = AVAssetExportPresetPassthrough | |
if verifyPresetForAsset(preferredPreset, asset) { | |
let composition = AVMutableComposition() | |
let videoCompTrack = composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID()) | |
let audioCompTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID()) | |
let assetVideoTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeVideo).first as! AVAssetTrack | |
let assetAudioTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeAudio).first as! AVAssetTrack | |
var compError: NSError? | |
var accumulatedTime = kCMTimeZero | |
for (startTimeForCurrentSlice, endTimeForCurrentSlice) in trimPoints { | |
let durationOfCurrentSlice = CMTimeSubtract(endTimeForCurrentSlice, startTimeForCurrentSlice) | |
let timeRangeForCurrentSlice = CMTimeRangeMake(startTimeForCurrentSlice, durationOfCurrentSlice) | |
videoCompTrack.insertTimeRange(timeRangeForCurrentSlice, ofTrack: assetVideoTrack, atTime: accumulatedTime, error: &compError) | |
audioCompTrack.insertTimeRange(timeRangeForCurrentSlice, ofTrack: assetAudioTrack, atTime: accumulatedTime, error: &compError) | |
if compError != nil { | |
NSLog("error during composition: \(compError)") | |
if let completion = completion { | |
completion(compError) | |
} | |
} | |
accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice) | |
} | |
let exportSession = AVAssetExportSession(asset: composition, presetName: preferredPreset) | |
exportSession.outputURL = destinationURL | |
exportSession.outputFileType = AVFileTypeAppleM4V | |
exportSession.shouldOptimizeForNetworkUse = true | |
removeFileAtURLIfExists(destinationURL) | |
exportSession.exportAsynchronouslyWithCompletionHandler({ () -> Void in | |
if let completion = completion { | |
completion(exportSession.error) | |
} | |
}) | |
} else { | |
NSLog("Could not find a suitable export preset for the input video") | |
let error = NSError(domain: "org.linuxguy.VideoLab", code: -1, userInfo: nil) | |
if let completion = completion { | |
completion(error) | |
} | |
} | |
} | |
let sourceURL = NSURL(fileURLWithPath: "/Users/acj/scratch/TestVideo.mp4") | |
let destinationURL = NSURL(fileURLWithPath: "/Users/acj/scratch/TrimmedVideo.mp4") | |
let sem = dispatch_semaphore_create(0) | |
if let sourceURL = sourceURL, destinationURL = destinationURL { | |
let timeScale: Int32 = 1000 | |
let trimPoints = [(CMTimeMake(2000, timeScale), CMTimeMake(5000, timeScale)), | |
(CMTimeMake(20500, timeScale), CMTimeMake(23000, timeScale)), | |
(CMTimeMake(60000, timeScale), CMTimeMake(65000, timeScale))] | |
trimVideo(sourceURL, destinationURL, trimPoints) { error in | |
if let error = error { | |
NSLog("Failure: \(error)") | |
} else { | |
NSLog("Success") | |
} | |
dispatch_semaphore_signal(sem) | |
} | |
} else { | |
NSLog("error: Please check the source and destination paths.") | |
} | |
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment