【问题标题】:AVFoundation: Fit Video to CALayer correctly when exportingAVFoundation:导出时正确地将视频适合 CALayer
【发布时间】:2015-11-19 14:03:59
【问题描述】:

问题:

我在获取使用 AVFoundation 创建的视频以在 VideoLayer 中显示时遇到问题,CALayer,尺寸正确。

示例:

这是视频的外观(在应用中显示给用户)

但是,导出后的视频如下:

详情

如您所见,它是一个方形视频,背景为绿色,视频适合指定帧。但是,生成的视频不适合用于包含它的CALayer(请参阅视频应拉伸到的黑色空间?)。

有时视频确实填充了图层,但被拉伸超出了边界(宽度过大或高度过大),并且通常无法保持视频的自然宽高比。

代码

CGRect displayedFrame = [self adjustedVideoBoundsFromVideo:gifVideo];//the cropped frame
CGRect renderFrame = [self renderSizeForGifVideo:gifVideo]; //the full rendersize
AVAsset * originalAsset = self.videoAsset;

AVAssetTrack * videoTrack = [[originalAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVMutableComposition * mainComposition = [AVMutableComposition composition];

AVMutableCompositionTrack * compositionTrack = [mainComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

[compositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, originalAsset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil];

CALayer * parentLayer = [CALayer layer];
CALayer * backgroundLayer = [CALayer layer];
CALayer * videoLayer = [CALayer layer];
parentLayer.frame = renderFrame;
backgroundLayer.frame = parentLayer.bounds;
backgroundLayer.backgroundColor = self.backgroundColor.CGColor;
videoLayer.frame = displayedFrame;
[parentLayer addSublayer:backgroundLayer];
[parentLayer addSublayer:videoLayer];


AVMutableVideoComposition * videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.frameDuration = CMTimeMake(1, 30);
videoComposition.renderSize = CGSizeMake(renderFrame.size.width, renderFrame.size.height);




videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool
                         videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];



AVMutableVideoCompositionInstruction * instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mainComposition.duration);

AVMutableVideoCompositionLayerInstruction * layerInstruction = [AVMutableVideoCompositionLayerInstruction
                                                                videoCompositionLayerInstructionWithAssetTrack:videoTrack];


instruction.layerInstructions = @[layerInstruction];
videoComposition.instructions = @[instruction];



NSString* videoName = @"myNewGifVideo.mp4";

NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:videoName];
NSURL    *exportUrl = [NSURL fileURLWithPath:exportPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath])
{
    [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
}

AVAssetExportSession * exporter = [[AVAssetExportSession alloc] initWithAsset:mainComposition presetName:AVAssetExportPresetHighestQuality];
exporter.videoComposition = videoComposition;
exporter.outputFileType = AVFileTypeMPEG4;
exporter.outputURL = exportUrl;

[exporter exportAsynchronouslyWithCompletionHandler:^
 {
     dispatch_async(dispatch_get_main_queue(), ^{
         self.finalVideo = exportUrl;
         [self.delegate shareManager:self didCreateVideo:self.finalVideo];
         if (completionBlock){
             completionBlock();
         }
     });
 }];

我的尝试:

我尝试调整videoLayerframeboundscontentGravity,但没有任何用处。

我尝试在AVMutableVideoCompositionLayerInstruction 中添加transform 以将视频缩放到displayRect 的大小(可以选择许多不同的视频,它们的宽度和高度是可变的。每个视频在结果视频,没有一个是正确的)转换有时会得到一个正确的维度(通常是宽度),但会弄乱另一个维度。如果我以稍微不同的方式裁剪/缩放视频,它永远不会始终正确地获得一个维度。

我尝试更改 videoCompositionrenderSize 但这会破坏方形裁剪。

我似乎无法正确理解。如何让视频用displayedFrame 帧完美填充videoLayer(最后注意:视频的naturalSizedisplayedFrame 不同,这就是我尝试对其进行转换的原因)?

【问题讨论】:

    标签: ios objective-c core-animation avfoundation calayer


    【解决方案1】:

    当视频在您的videoLayer 中渲染时,它应用了一个隐式变换t(我们无权访问它,但这是渲染工具在内部应用到视频的一些初始变换)。为了使视频在导出时完全填充该层,我们必须了解初始变换的来源。 t 表现出一种奇怪的行为:它取决于您的视频合成的renderSize(在您的示例中,这将是一个正方形)。您可以看到,如果您将renderSize 设置为其他任何值,则在videoLayer 中呈现的视频的比例和纵横比也会发生变化——即使您根本没有更改videoLayerframe。我看不出这种行为有什么意义(合成的帧和作为合成一部分的视频层的帧应该是完全独立的),所以我认为这是AVVideoCompositionCoreAnimationTool 中的一个错误。

    要纠正 t 的不祥行为,请将以下转换应用于您的 videoInstruction

    let bugFixTransform = CGAffineTransform(scaleX: renderSize.width/videoTrack.naturalSize.width,
                                            y: renderSize.height/videoTrack.naturalSize.height)
    videoLayerInstruction.setTransform(bugFixTransform, at: .zero)
    

    然后视频将完全填满videoLayer

    如果视频没有标准方向,则必须应用另外两个变换来固定方向和比例:

    let orientationAspectTransform: CGAffineTransform
    let sourceVideoIsRotated: Bool = videoTrack.preferredTransform.a == 0
    if sourceVideoIsRotated {
      orientationAspectTransform = CGAffineTransform(scaleX: videoTrack.naturalSize.width/videoTrack.naturalSize.height,
                                                     y: videoTrack.naturalSize.height/videoTrack.naturalSize.width)
    } else {
      orientationAspectTransform = .identity
    }
    
    let bugFixTransform = CGAffineTransform(scaleX: compositionSize.width/videoTrack.naturalSize.width,
                                            y: compositionSize.height/videoTrack.naturalSize.height)
    let transform =
      videoTrack.preferredTransform
        .concatenating(bugFixTransform)
        .concatenating(orientationAspectTransform)
    videoLayerInstruction.setTransform(transform, at: .zero)
    

    更新:仅针对您使用 preferredTransform 的情况,请注意其中一个也已损坏(包括 iOS 14),并且在某些情况下可能会导致意外转换,请参阅 @987654321 @ 获取描述和解决方法。

    【讨论】:

    • 你是我的绝对英雄! +1
    【解决方案2】:

    斯威夫特 3

    let mainComposition = AVMutableVideoComposition()
    mainComposition.renderSize = CGSize(width: yourWidth, height: yourHeight)
    

    【讨论】:

      【解决方案3】:

      由于相机记录的纵横比,您正面临这个问题。录制时通过设置内容重力填充AVCaptureVideoPreviewLayer,但预览层与摄像机录制的纵横比无关。它只是设置您在录制时看到的内容。从您发布的图片可以看出相机正在以 16:9 的比例录制。因此,要么您必须以该比率设置图层的边界,要么使用视频的帧来转换它们。 对于第二种方法,您需要在您的AVMutableVideoCompositionLayerInstruction 上使用- (void)setTransform:(CGAffineTransform)transform atTime:(CMTime)time

      你必须根据你的层边界自己形成变换。它有点尝试和错误,因为它将是一些CGAffineTransformMakeTranslationCGAffineTransformMakeScale 的组合。所以对于贴图的案例,需要放大翻译。如果界限超出,那么您必须缩小。

      【讨论】:

        猜你喜欢
        • 2018-04-07
        • 2023-04-02
        • 2018-04-29
        • 2020-07-15
        • 2011-06-12
        • 1970-01-01
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多