Merge videos

hi,


I need to merge two or more videos after cutting them from one big video, I can cut them without no problems, after that I merge the two videos created before. The problem is in the final result video, when I see the this video the duration is correct but it only plays the first video and for the duration of the second video remains a static image. For example: two videos of 6 seconds each makes a video of 12 seconds, i can see it correctly until 6 seconds, later it blocks at the last frame of first video.


Here is the code of my merge function.


Can anyone help me ?


func mergeVideos(videoMergedUrl:URL) {
  let mainComposition = AVMutableVideoComposition()
  var startDuration:CMTime = kCMTimeZero
  let mainInstruction = AVMutableVideoCompositionInstruction()
  let mixComposition = AVMutableComposition()
  var allVideoInstruction = [AVMutableVideoCompositionLayerInstruction]()

  for i:Int in 0 ..< listSegment.count {
  let currentAsset = listSegment[i]
  let currentTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
  do {
  try currentTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, currentAsset.duration), of: currentAsset.tracks(withMediaType: AVMediaType.video)[0], at: startDuration)
  let currentInstruction:AVMutableVideoCompositionLayerInstruction = videoCompositionInstructionForTrack(currentTrack!, asset: currentAsset)
  

  allVideoInstruction.append(currentInstruction) //Add video instruction in Instructions Array.
  startDuration = CMTimeAdd(startDuration, currentAsset.duration)
  } catch _ {
  print("ERROR_LOADING_VIDEO")
  }
  }
  mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, startDuration)
  mainInstruction.layerInstructions = allVideoInstruction

  mainComposition.instructions = [mainInstruction]
  mainComposition.frameDuration = CMTimeMake(1, 30)
  mainComposition.renderSize = CGSize(width: 640, height: 480)

  let manager = FileManager.default
  _ = try? manager.removeItem(at: videoMergedUrl)

  guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPreset640x480) else { return }
  exporter.outputURL = videoMergedUrl
  exporter.outputFileType = AVFileType.mp4
  exporter.shouldOptimizeForNetworkUse = false
  exporter.videoComposition = mainComposition

  // Perform the Export
  exporter.exportAsynchronously() {
  DispatchQueue.main.async {
  self.exportDidFinish(exporter)
  }
  }
}

Replies

The same tutorial on how to merge videos:


h ttps://www.raywenderlich.com/188034/how-to-play-record-and-merge-videos-in-ios-and-swift


Thanks to tell if that answers your question ; if so, mark the answer as correct to close the threads

Thank for your help, but this tutorial is not enough for me, I've began to study from this but I have the same problem.


Do you have any other tutorial for me ?

Thank for your help, but this tutorial is not enough for me, I've began to study from this but I have the same problem.


Do you have any other tutorial for me ?

When I compare your code with the tutorial, I do not see where you manage the 2 tracks to merge.

I see only one track created with

let currentTrack = mixComposition.addMutableTrack(…)

Don't know if that's the problem, maybe I've just poorly understood your code


guard
  let firstAsset = firstAsset,
  let secondAsset = secondAsset
  else {
    return
}

activityMonitor.startAnimating()

// 1 - Create AVMutableComposition object. This object will hold your AVMutableCompositionTrack instances.
let mixComposition = AVMutableComposition()

// 2 - Create two video tracks
guard
  let firstTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video,
                                                  preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
  else {
    return
}
do {
  try firstTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, firstAsset.duration),
                                 of: firstAsset.tracks(withMediaType: AVMediaType.video)[0],
                                 at: kCMTimeZero)
} catch {
  print("Failed to load first track")
  return
}

guard
  let secondTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video,
                                                   preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
  else {
    return
}
do {
  try secondTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, secondAsset.duration),
                                  of: secondAsset.tracks(withMediaType: AVMediaType.video)[0],
                                  at: firstAsset.duration)
} catch {
  print("Failed to load second track")
  return
}

// 3 - Audio track
if let loadedAudioAsset = audioAsset {
  let audioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: 0)
  do {
    try audioTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, CMTimeAdd(firstAsset.duration,secondAsset.duration)),
                                    of: loadedAudioAsset.tracks(withMediaType: AVMediaType.audio)[0] ,
                                    at: kCMTimeZero)
  } catch {
    print("Failed to load Audio track")
  }
}

// 4 - Get path
guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, .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")


// 5 - Create Exporter
guard let exporter = AVAssetExportSession(asset: mixComposition,
                                          presetName: AVAssetExportPresetHighestQuality) else {
  return
}
exporter.outputURL = url
exporter.outputFileType = AVFileType.mov
exporter.shouldOptimizeForNetworkUse = true

// 6 - Perform the Export
exporter.exportAsynchronously() {
  DispatchQueue.main.async {
    self.exportDidFinish(exporter)
  }
}

will this code work also on downloaded HLS content(pkg files) ?