I am working on a project wherein I am trying to merge two AVAssets (video files) together using an AVMutableComposition
. My video files, when saved to my Camera Roll, are both exactly as expected. Their URL is valid, but yet my final exported product only shows the first video, and not the second merged video. This is the code I am working with:
// Setup video asset
let videoAsset: AVAsset = AVAsset( url: clip1 )
// Setup composition
let composition = AVMutableComposition()
// Get video track
let vtrack = videoAsset.tracks(withMediaType: AVMediaType.video)
// Setup the first video track as asset track
let videoTrack: AVAssetTrack = vtrack[0]
// Setup the video timerange
let vid_timerange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
// Setup the composition video track
let compositionvideoTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)!
// Insert expected time range
do {
try compositionvideoTrack.insertTimeRange(vid_timerange, of: videoTrack, at: kCMTimeZero)
} catch {
print("An error occurred")
}
// Setup second video asset
let reversedAsset: AVAsset = AVAsset( url: clip2 )
// Setup the video track
let vtrack1 = reversedAsset.tracks(withMediaType: AVMediaType.video)
// Setup the video track
let videoTrack1: AVAssetTrack = vtrack1[0]
// Setup the video time range
let vid1_timerange = CMTimeRangeMake(kCMTimeZero, reversedAsset.duration)
// Setup the second composition video track
let secondCompositionVideoTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)!
// Insert time range
do {
try secondCompositionVideoTrack.insertTimeRange(vid1_timerange, of: videoTrack1, at: videoAsset.duration)
} catch {
print("An error occurred")
}
// Setup the folder path
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
// Setup documents directory
let documentsDirectory = paths[0] as String
// Setup the last path component
let lastPath = clip1.lastPathComponent
// Setup the reverse string
let reverseString = "loop-" + lastPath
// Setup desired full path
let fullPath: String = "\(documentsDirectory)/\(reverseString)"
// Setup reverse destination URL
let reverseURL = URL(fileURLWithPath: fullPath)
// Export
let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)!
// Setup the destination for output
exportSession.outputURL = reverseURL
// Setup the file type
exportSession.outputFileType = AVFileType.mp4
exportSession.exportAsynchronously(completionHandler: {
if exportSession.status == .completed {
// Send completion on main queue
DispatchQueue.main.async(execute: {
PHPhotoLibrary.shared().performChanges({PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: reverseURL)
}) { saved, error in
if saved {
print("save of potentially looped video succesful")
}
}
// Send completion handler
completionHandler(reverseURL)
})
return
} else if exportSession.status == .failed {
print("Loop Export failed - \(String(describing: exportSession.error))")
completionHandler(nil)
}
completionHandler(nil)
return
})
What's being saved to my Camera Roll is only the first clip, with no evidence of the second clip in sight. Any help would be greatly appreciated. Thanks!
Your second clip starts at time zero when it should start at the first clip’s duration:
let vid1_timerange = CMTimeRangeMake(videoAsset.duration, reversedAsset.duration)