-
-
Save jakebromberg/098c328d87bd25ec0ae693b877cb933c 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
import AVFoundation | |
import Foundation | |
extension FileManager { | |
func removeFileIfNecessary(at url: URL) throws { | |
guard fileExists(atPath: url.path) else { | |
return | |
} | |
do { | |
try removeItem(at: url) | |
} | |
catch let error { | |
throw TrimError("Couldn't remove existing destination file: \(error)") | |
} | |
} | |
} | |
struct TrimError: Error { | |
let description: String | |
let underlyingError: Error? | |
init(_ description: String, underlyingError: Error? = nil) { | |
self.description = "TrimVideo: " + description | |
self.underlyingError = underlyingError | |
} | |
} | |
extension AVMutableComposition { | |
convenience init(asset: AVAsset) { | |
self.init() | |
for track in asset.tracks { | |
addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID) | |
} | |
} | |
func trim(timeOffStart: Double) { | |
let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1) | |
let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration) | |
for track in tracks { | |
track.removeTimeRange(timeRange) | |
} | |
removeTimeRange(timeRange) | |
} | |
} | |
extension AVAsset { | |
func assetByTrimming(timeOffStart: Double) throws -> AVAsset { | |
let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1) | |
let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration) | |
let composition = AVMutableComposition() | |
do { | |
for track in tracks { | |
let compositionTrack = composition.addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID) | |
try compositionTrack?.insertTimeRange(timeRange, of: track, at: kCMTimeZero) | |
} | |
} catch let error { | |
throw TrimError("error during composition", underlyingError: error) | |
} | |
return composition | |
} | |
func export(to destination: URL) throws { | |
guard let exportSession = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetPassthrough) else { | |
throw TrimError("Could not create an export session") | |
} | |
exportSession.outputURL = destination | |
exportSession.outputFileType = AVFileType.m4v | |
exportSession.shouldOptimizeForNetworkUse = true | |
let group = DispatchGroup() | |
group.enter() | |
try FileManager.default.removeFileIfNecessary(at: destination) | |
exportSession.exportAsynchronously { | |
group.leave() | |
} | |
group.wait() | |
if let error = exportSession.error { | |
throw TrimError("error during export", underlyingError: error) | |
} | |
} | |
} | |
func time(_ operation: () throws -> ()) rethrows { | |
let start = Date() | |
try operation() | |
let end = Date().timeIntervalSince(start) | |
print(end) | |
} | |
let sourceURL = URL(fileURLWithPath: CommandLine.arguments[1]) | |
let destinationURL = URL(fileURLWithPath: CommandLine.arguments[2]) | |
do { | |
try time { | |
let asset = AVURLAsset(url: sourceURL) | |
let trimmedAsset = try asset.assetByTrimming(timeOffStart: 1.0) | |
try trimmedAsset.export(to: destinationURL) | |
} | |
} catch let error { | |
print("💩 \(error)") | |
} |
guys am trying to write a function that can take a video URL and return the same video chopped in pieces each piece 10 sec long, any idea how to do it?
thanx
Hey @jakebromberg, thanks for the snippets!
assetByTrimming
turns vertical videos into horizontal videos. Do you know why?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for the great snippet!
func time(_ operation: () throws -> ()) rethrows
has misplaced brakets.Also you can describe that it is a test function and to use your code people must use:
removeFileIfNecessary i think should be commented in most cases =)
Also i've changed the
Hope this helps to somebody)