合并视频(AVFoundation)

时间:2013-08-30 19:29:12

标签: ios objective-c avfoundation

在我的应用中,我正在录制小视频并将其添加到NSMutableArray作为AVAsset,以便记录已捕获的内容。当用户按下按钮合并它们时,最终结果只是拍摄的第一个视频(例如,如果拍摄了三个短视频,合并后的最终结果只是第一个视频而其他视频没有出现)。我在NSMutableArray中迭代并将视频拼接在一起的代码在这里:

if (self.capturedVideos.count != 0) {        
    //Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
    AVMutableComposition* mixComposition = [[AVMutableComposition alloc] init];

    for (AVAsset *asset in self.capturedVideos) {
        //check if the video is the first one captures so that it  is placed at time 0.
        if ([self.capturedVideos indexOfObject:asset] == 0) {
            AVMutableCompositionTrack *firstTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
            [firstTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:nil];
            previousAsset = asset;
        } else{
            AVMutableCompositionTrack *track = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
            [track insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:previousAsset.duration error:nil];
            previousAsset = asset;
        }
    }

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *myPathDocs =  [documentsDirectory stringByAppendingPathComponent:
    [NSString stringWithFormat:@"mergeVideo-%d.mov",arc4random() % 1000]];
    NSURL *url = [NSURL fileURLWithPath:myPathDocs];
    // 5 - Create exporter
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
    exporter.outputURL=url;
    exporter.outputFileType = AVFileTypeQuickTimeMovie;
    exporter.shouldOptimizeForNetworkUse = YES;
    [exporter exportAsynchronouslyWithCompletionHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [self exportDidFinish:exporter];
        });
    }];
}

for循环后的内容是导出要保存在相机胶卷中的视频。那我的错误在哪里?持续时间是正确的(所以没有重叠)。但是,我怀疑某事。在@括号中的@implementation之后添加了一个实例变量,它是previousAsset,它跟踪添加的先前资产,从而知道下一个放置的位置。这是AVAsset类,所以我没有初始化它,因为当我尝试它给我一个错误。

  

previousAsset = [[AVAsset alloc] init];

4 个答案:

答案 0 :(得分:3)

这样可以正常使用

      AVMutableComposition *mainComposition = [[AVMutableComposition alloc] init];
      AVMutableCompositionTrack *compositionVideoTrack = [mainComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];


      AVMutableCompositionTrack *soundtrackTrack = [mainComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
      CMTime insertTime = kCMTimeZero;

      for (AVAsset *videoAsset in assets) {

          [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:insertTime error:nil];

          [soundtrackTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:insertTime error:nil];

          // Updating the insertTime for the next insert
          insertTime = CMTimeAdd(insertTime, videoAsset.duration);
      }

      NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
      NSString *documentsDirectory = [paths objectAtIndex:0];

      // Creating a full path and URL to the exported video
      NSString *outputVideoPath =  [documentsDirectory stringByAppendingPathComponent:
                              [NSString stringWithFormat:@"mergeVideo-%d.mov",arc4random() % 1000]];

      // NSString *documentsDirectory = [paths objectAtIndex:0];
      NSString *myPathDocs =  [documentsDirectory stringByAppendingPathComponent:
                         current_name];
      NSURL *outptVideoUrl = [NSURL fileURLWithPath:myPathDocs];
      AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mainComposition presetName:AVAssetExportPreset640x480];

      // Setting attributes of the exporter
      exporter.outputURL=outptVideoUrl;
      exporter.outputFileType =AVFileTypeMPEG4;   //AVFileTypeQuickTimeMovie;
      exporter.shouldOptimizeForNetworkUse = YES;
      [exporter exportAsynchronouslyWithCompletionHandler:^{
          dispatch_async(dispatch_get_main_queue(), ^{
              //completion(exporter);
              [self exportDidFinish:exporter];
              // [self exportDidFinish:exporter:assets];
          });
      }];

这样可以正常工作..

答案 1 :(得分:3)

Swift版本

pto_requests

答案 2 :(得分:1)

更新了@brenoxp的Swift 5.1答案

func merge(arrayVideos:[AVAsset], completion:@escaping (URL?, Error?) -> ()) {

  let mainComposition = AVMutableComposition()
  let compositionVideoTrack = mainComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
  compositionVideoTrack?.preferredTransform = CGAffineTransform(rotationAngle: .pi / 2)

  let soundtrackTrack = mainComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)

    var insertTime = CMTime.zero

  for videoAsset in arrayVideos {
    try! compositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration), of: videoAsset.tracks(withMediaType: .video)[0], at: insertTime)
    try! soundtrackTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration), of: videoAsset.tracks(withMediaType: .audio)[0], at: insertTime)

    insertTime = CMTimeAdd(insertTime, videoAsset.duration)
  }

  let outputFileURL = URL(fileURLWithPath: NSTemporaryDirectory() + "merge.mp4")

  let fileManager = FileManager()
  try? fileManager.removeItem(at: outputFileURL)

  let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality)

  exporter?.outputURL = outputFileURL
  exporter?.outputFileType = AVFileType.mp4
  exporter?.shouldOptimizeForNetworkUse = true

  exporter?.exportAsynchronously {
    if let url = exporter?.outputURL{
        completion(url, nil)
    }
    if let error = exporter?.error {
        completion(nil, error)
    }
  }
}

答案 3 :(得分:0)

Git Hub上有一个很棒的示例项目,这是一个很好的起点,它是如何在应用程序中以更可重用的方式执行此操作的

https://github.com/khoavd-dev/MergeVideos/blob/master/MergeVideos/VideoManager/KVVideoManager.swift