使用AVMutableVideoCompositionLayerInstruction旋转视频

时间:2011-03-03 23:11:13

标签: iphone ios video-capture avfoundation

我正在使用前置摄像头在iPhone 4上拍摄视频,并将视频与其他媒体资产相结合。我希望这个视频是纵向的 - 所有视频的默认方向都是横向的,在某些情况下,你必须手动管理。

我正在使用AVFoundation,特别是AVAssetExportSession和AVMutableVideoComposition。基于WWDC视频,当我将视频组合成新的合成时,我必须自己处理“修正”方向。

所以,我已经创建了一个附加到我的AVMutableVideoCompositionInstruction的AVMutableVideoCompositionLayerInstruction,我正在使用setTransform:atTime:方法设置一个用于旋转视频的转换:

    AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
CGAffineTransform portraitRotationTransform = CGAffineTransformMakeRotation(degreesToRadians(90.0));
[passThroughLayer setTransform:portraitRotationTransform atTime:kCMTimeZero];

问题在于,当我查看导出的视频时,屏幕上没有任何实际内容。如果我将旋转角度减小到45度,我可以在屏幕上看到部分视频 - 它几乎就像它不在中心点旋转一样。我在下面包含图片,以便更清楚地说明我在说什么。

视频的自然尺寸将恢复为480x360。我已经尝试将其更改为360x480并且它不会影响核心问题。

0度轮换:

enter image description here

45度轮换:

enter image description here

90度旋转只是全绿色。

无论如何,我希望之前做过此事的人可以指出我正确的方向。我找不到关于AVFoundation合成和出口中一些更高级主题的任何文档。

5 个答案:

答案 0 :(得分:11)

以目前为止的答案为基础。我发现了一种非常好的调试方法,可以找出变换出了什么问题。使用可用的渐变方法,您可以设置变换的动画,以便更容易地查看转换正在执行的操作。

大多数时候,我发现自己的变换似乎什么也没做,直到我意识到仅仅使用视频轨道的preferredTransform属性可能会导致视频输出移出渲染屏幕。

AVMutableVideoCompositionLayerInstruction *videoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];

[videoLayerInstruction setTransformRampFromStartTransform:CGAffineTransformIdentity
toEndTransform:videoTrack.preferredTransform 
timeRange:CMTimeRangeMake(projectClipStart, projectClipDuration)];

最终,我发现在某些情况下,我需要应用翻译以将旋转的视频带回渲染屏幕。

CGAffineTransformConcat(videoTrack.preferredTransform, CGAffineTransformMakeTranslation(0, renderSize.height))

注意:您的翻译价值可能会有所不同。

答案 1 :(得分:6)

试试这个:

AVMutableVideoCompositionLayerInstruction *passThroughLayer = AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(degreesToRadians(90.0));
CGAffineTransform rotateTranslate = CGAffineTransformTranslate(rotateTransform,320,0);
[passThroughLayer setTransform:rotateTranslate atTime:kCMTimeZero];

本质上,我们的想法是创建一个旋转和平移矩阵。您将其旋转到正确的方向,然后将其转换为视图。当我浏览API时,我没有看到任何指定中心点的方法。

答案 2 :(得分:2)

AVAssetTrack *videoAssetTrack= [[videoAsset tracksWithMediaType:AVMediaTypeVideo] lastObject];

AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:&error];

videoCompositionTrack.preferredTransform = videoAssetTrack.preferredTransform;

答案 3 :(得分:1)

也可能相关,但在使用前置摄像头时,preferredTransform中存在一个错误。例如,请参阅此处,SDAVAssetExportSession项目中的人员编写了一个解决方法:

https://github.com/rs/SDAVAssetExportSession/pull/70

答案 4 :(得分:0)

我在Flutter插件项目中找到了解决方案。

- (CGAffineTransform)fixTransform:(AVAssetTrack*)videoTrack {
  CGAffineTransform transform = videoTrack.preferredTransform;

  if (transform.tx == 0 && transform.ty == 0) {
      NSInteger rotationDegrees = (NSInteger)round(radiansToDegrees(atan2(transform.b, transform.a)));
      NSLog(@"TX and TY are 0. Rotation: %ld. Natural width,height: %f, %f", (long)rotationDegrees,
      videoTrack.naturalSize.width, videoTrack.naturalSize.height);
      if (rotationDegrees == 90) {
        NSLog(@"Setting transform tx");
        transform.tx = videoTrack.naturalSize.height;
        transform.ty = 0;
      } else if (rotationDegrees == 270) {
        NSLog(@"Setting transform ty");
        transform.tx = 0;
        transform.ty = videoTrack.naturalSize.width;
     }
  }
  return transform;
}

// set layerInstruction
[firstVideoLayerInstruction setTransform:[self fixTransform:firstVideoAssetTrack] atTime:kCMTimeZero];

Flutter VideoPlayerPlugin