Swift: Merge Videos back-to-back with AVAssetExportSession()
Asked Answered
F

1

6

I have an array of video recordings stored as AVURLAsset and I want to merge the videos into 1 clip so that they play back to back with no time in-between videos. Below is what I'm hoping to do, but it is only showing the 1st video. Note: I want the videos to play back-to-back and without blank space between each. For example, if there is real time between recording1 and recording2 when the phone was not recording, the output should NOT show this.

Thanks for your help!

func mergeVideos(handler: @escaping (_ asset: AVAssetExportSession)->()) {
    let mixComposition = AVMutableComposition()
    var videoTime = CMTime.zero
    for video in self.recordings {

        let tracks = video.tracks(withMediaType: .video)
        let videoTrack = tracks[0]
        let videoTimeRange = CMTimeRangeMake(start: .zero, duration: video.duration)
        guard let compositionVideoTrack: AVMutableCompositionTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
        do {
            try compositionVideoTrack.insertTimeRange(videoTimeRange, of: videoTrack, at: videoTime)
            videoTime = CMTimeAdd(videoTime, video.duration)
        } catch {
            print("An error occurred")
            return
        }
    }

    guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .long
    dateFormatter.timeStyle = .short
    let date = dateFormatter.string(from: Date())
    let url = documentDirectory.appendingPathComponent("mergeVideo-\(date).mov")

    guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
    exporter.outputURL = url
    exporter.outputFileType = AVFileType.mov
    exporter.shouldOptimizeForNetworkUse = true
    handler(exporter)
}
Fredenburg answered 16/3, 2020 at 21:28 Comment(0)
F
12

The solution is to move the AVMutableCompositionTrack outside of the for loop. My updated function to merge videos (with audio) is below. Hope this helps someone!

var recordings = [AVURLAsset]()

func mergeVideos(handler: @escaping (_ asset: AVAssetExportSession)->()) {
    let videoComposition = AVMutableComposition()
    var lastTime: CMTime = .zero

    guard let videoCompositionTrack = videoComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
    guard let audioCompositionTrack = videoComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }

    for video in self.recordings {
        //add audio/video
        do {
            try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: video.duration), of: video.tracks(withMediaType: .video)[0], at: lastTime)
            try audioCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: video.duration), of: video.tracks(withMediaType: .audio)[0], at: lastTime)

        } catch {
            print("Failed to insert audio or video track")
            return
        }

        //update time
        lastTime = CMTimeAdd(lastTime, video.duration)
    }

    //create url
    guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .long
    dateFormatter.timeStyle = .short
    let date = dateFormatter.string(from: Date())
    let url = documentDirectory.appendingPathComponent("mergeVideo-\(date).mov")

    //export
    guard let exporter = AVAssetExportSession(asset: videoComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
    exporter.outputURL = url
    exporter.outputFileType = AVFileType.mp4
    exporter.shouldOptimizeForNetworkUse = true
    handler(exporter)
}
Fredenburg answered 17/3, 2020 at 17:32 Comment(1)
worked on the first try, thank you!Yl

© 2022 - 2025 — McMap. All rights reserved.