2016-08-16 2 views
2

Ich möchte die AVAsset-arrayVideos in einem einzigen Video zusammenführen und speichern Sie es auf Kamera rollen. Raywenderlich.com hat eine tolle tutorial, wo zwei Videos zu einem zusammengeführt werden. Ich habe den folgenden Code erstellt, jedoch enthält das Video, das ich nach dem Export in die Kamerarolle erhalte, nur das erste und das letzte Video aus dem Array (mit Ausnahme der restlichen Videos in der Mitte von arrayVideos). Fehle ich hier etwas?Swift Merge AVasset-Videos Array

var arrayVideos = [AVAsset]() //Videos Array  
var atTimeM: CMTime = CMTimeMake(0, 0) 
var lastAsset: AVAsset! 
var layerInstructionsArray = [AVVideoCompositionLayerInstruction]() 
var completeTrackDuration: CMTime = CMTimeMake(0, 1) 
var videoSize: CGSize = CGSize(width: 0.0, height: 0.0) 

func mergeVideoArray(){ 

    let mixComposition = AVMutableComposition() 
    for videoAsset in arrayVideos{ 
     let videoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) 
     do { 
      if videoAsset == arrayVideos.first{ 
       atTimeM = kCMTimeZero 
      } else{ 
       atTimeM = lastAsset!.duration 
      } 
      try videoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0], at: atTimeM) 
      videoSize = videoTrack.naturalSize 
     } catch let error as NSError { 
      print("error: \(error)") 
     } 
     completeTrackDuration = CMTimeAdd(completeTrackDuration, videoAsset.duration) 
     let videoInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack) 
     if videoAsset != arrayVideos.last{ 
      videoInstruction.setOpacity(0.0, at: videoAsset.duration) 
     } 
     layerInstructionsArray.append(videoInstruction) 
     lastAsset = videoAsset    
    } 

    let mainInstruction = AVMutableVideoCompositionInstruction() 
    mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, completeTrackDuration) 
    mainInstruction.layerInstructions = layerInstructionsArray   

    let mainComposition = AVMutableVideoComposition() 
    mainComposition.instructions = [mainInstruction] 
    mainComposition.frameDuration = CMTimeMake(1, 30) 
    mainComposition.renderSize = CGSize(width: videoSize.width, height: videoSize.height) 

    let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] 
    let dateFormatter = DateFormatter() 
    dateFormatter.dateStyle = .long 
    dateFormatter.timeStyle = .short 
    let date = dateFormatter.string(from: NSDate() as Date) 
    let savePath = (documentDirectory as NSString).appendingPathComponent("mergeVideo-\(date).mov") 
    let url = NSURL(fileURLWithPath: savePath) 

    let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) 
    exporter!.outputURL = url as URL 
    exporter!.outputFileType = AVFileTypeQuickTimeMovie 
    exporter!.shouldOptimizeForNetworkUse = true 
    exporter!.videoComposition = mainComposition 
    exporter!.exportAsynchronously { 

     PHPhotoLibrary.shared().performChanges({ 
      PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: exporter!.outputURL!) 
     }) { saved, error in 
      if saved { 
       let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert) 
       let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil) 
       alertController.addAction(defaultAction) 
       self.present(alertController, animated: true, completion: nil) 
      } else{ 
       print("video erro: \(error)") 

      } 
     } 
    } 
} 

Antwort

3

Sie müssen die Gesamtzeit für alle Assets verfolgen und für jedes Video aktualisieren.

Der Code in Ihrer Frage wurde die atTimeM mit dem aktuellen Video neu geschrieben. Deshalb wurden nur die ersten und letzten eingeschlossen.

Es wird wie folgt aussehen:

... 
var totalTime : CMTime = CMTimeMake(0, 0) 

func mergeVideoArray() { 

    let mixComposition = AVMutableComposition() 
    for videoAsset in arrayVideos { 
     let videoTrack = 
      mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, 
              preferredTrackID: Int32(kCMPersistentTrackID_Invalid))   
     do { 
      if videoAsset == arrayVideos.first { 
       atTimeM = kCMTimeZero 
      } else { 
       atTimeM = totalTime // <-- Use the total time for all the videos seen so far. 
      } 
      try videoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), 
              of: videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0], 
              at: atTimeM) 
      videoSize = videoTrack.naturalSize 
     } catch let error as NSError { 
      print("error: \(error)") 
     } 
     totalTime += videoAsset.duration // <-- Update the total time for all videos. 
... 

Sie können die Verwendung von lastAsset entfernen.

+0

Prost Daniel. Nur Ihre Antwort zu ergänzen, müssen wir die Gesamtzeit auch in dem folgenden Code hinzuzufügen: wenn videoAsset = arrayVideos.last { videoInstruction.setOpacity (0.0 unter: completeTrackDuration) }! – Marcelo

+0

Dank für das Hinzufügen des zusätzlichen Code. – Daniel

+0

Sie können totalTime nicht mit + = erhöhen, müssen Sie CMTimeAdd verwenden. – user3288724

0

Sie brauchen atTimeM überhaupt nicht, da Sie einfach entlang der komplettenTrackDuration marschieren, wo das nächste Stück hinzugefügt werden soll. So ersetzen

if videoAsset == arrayVideos.first{ 
      atTimeM = kCMTimeZero 
     } else{ 
      atTimeM = lastAsset!.duration 
     } 
     try videoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0], at: atTimeM) 

mit

try videoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0], at: completeTrackDuration)