将视频合并在一起(AVFoundation)

4

在我的应用程序中,我正在录制小视频,并将它们添加到一个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循环之后的代码是用于将视频导出并保存到相机胶卷中。那么我的错误在哪里呢?持续时间是正确的(因此没有重叠)。然而,我对某些事情表示怀疑。在@implementaion括号中添加了一个实例变量previousAsset,用于跟踪先前添加的资源,从而知道下一个资源应该放在哪里。它是AVAsset类的对象,因此我没有初始化它,因为当我尝试这样做时会显示错误。

previousAsset = [[AVAsset alloc] init];


尝试这个:https://dev59.com/OJffa4cB1Zd3GeqP1wjK#45296947 - Datt Patel
4个回答

6

Swift版本

func merge(arrayVideos:[AVAsset], completion:@escaping (_ exporter: AVAssetExportSession) -> ()) -> Void {

  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 = kCMTimeZero

  for videoAsset in arrayVideos {
    try! compositionVideoTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: videoAsset.tracks(withMediaType: .video)[0], at: insertTime)
    try! soundtrackTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, 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()
  fileManager.removeItemIfExisted(outputFileURL)

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

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

  exporter?.exportAsynchronously {
    DispatchQueue.main.async {
      completion(exporter!)
    }
  }
}

我如何访问合并后的视频? - user10817680
视频应该在exporter.outputURL处可用。 - user4713761

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];
          });
      }];

这将很好地发挥作用。

2

更新了@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)
    }
  }
}

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接