2016-08-16 6 views
2

AVAsset-arrayVideos을 하나의 비디오로 병합하고 카메라 롤에 저장하고 싶습니다. Raywenderlich.com은 두 개의 동영상이 하나로 합쳐지는 tutorial을 가지고 있습니다. 다음 코드를 만들었지 만 카메라 롤로 내보내기 한 후 가져 오는 비디오에는 배열의 첫 번째 및 마지막 비디오 만 포함됩니다 (나머지 비디오는 arrayVideos 중간 부분 제외). 내가 여기서 뭔가를 놓치고 있니?스위프트 병합 AVasset- 비디오 배열

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)") 

      } 
     } 
    } 
} 

답변

3

모든 애셋의 총 시간을 추적하고 각 동영상에 대해 업데이트해야합니다.

질문에있는 코드는 현재 비디오로 atTimeM을 다시 쓰고 있습니다. 그래서 첫 번째와 마지막 만 포함되었습니다.

그것은 다음과 같이 보일 것입니다 :

... 
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. 
... 

당신은 lastAsset의 사용을 제거 할 수 있습니다.

+0

건배 다니엘과

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) 

를 교체합니다. 그냥 답을 보완하기 위해, 우리는 다음과 같은 코드도 총 시간을 추가해야합니다 경우 videoAsset = arrayVideos.last {(0.0 : completeTrackDuration) videoInstruction.setOpacity }! 추가 코드를 추가 – Marcelo

+0

감사합니다. – Daniel

+0

+ =로 totalTime을 늘릴 수 없으면 CMTimeAdd를 사용해야합니다. – user3288724

0

atTimeM이 필요하지 않습니다. 단순히 다음 트랙을 추가해야하는 곳에서 completeTrackDuration을 실행하기 만하면됩니다. 그래서

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