[英]AVFoundation: Fit Video to CALayer correctly when exporting
问题:
我在获取我使用 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();
}
});
}];
我试过的:
我尝试调整videoLayer
的frame
、 bounds
和contentGravity
,但没有任何用处。
我尝试添加一个transform
到AVMutableVideoCompositionLayerInstruction
缩放视频的大小displayRect
(在所得到的视频可以选自许多不同的视频,并且其宽度和高度是可变的。每个视频节目不同,其中没有正确地)转化有时会得到正确的一个维度(通常是宽度),但弄乱了另一个维度。 如果我以稍微不同的方式裁剪/缩放视频,它永远不会始终保持正确的维度。
我试过改变videoComposition
的renderSize
但这破坏了方形裁剪。
我似乎无法正确理解。 我怎样才能获得视频完美地填补了videoLayer
与displayedFrame
架(最终注: naturalSize
从视频不同的displayedFrame
这就是为什么我试图转换它)?
当视频在您的videoLayer
渲染时,它应用了隐式转换t
(我们无权访问它,但这是渲染工具在内部应用于视频的一些初始转换)。 为了使视频在导出时完全填充该层,我们必须了解初始变换的来源。 t
显示出一种奇怪的行为:它取决于您的视频合成的renderSize
(在您的示例中,它将是一个正方形)。 您可以看到,如果将renderSize
设置为其他任何值,则videoLayer
呈现的视频的比例和纵横比videoLayer
发生变化 - 即使您根本没有更改videoLayer
的frame
。 我不明白这种行为有什么意义(合成的帧和作为合成一部分的视频层的帧应该完全独立),所以我认为这是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),并且在某些情况下可能会导致意外转换,请参阅此问题以获取说明和解决方法。
由于相机记录的纵横比,您面临这个问题。 在录制时,您通过设置内容重力来填充AVCaptureVideoPreviewLayer
,但预览层与相机录制的纵横比无关。 它只是设置您在录制时看到的内容。 从您发布的图片中可以看出相机正在以 16:9 的比例录制。 因此,您要么必须以此比例设置图层的边界,要么使用视频的帧来转换它们。 对于第二种方法,你需要使用- (void)setTransform:(CGAffineTransform)transform atTime:(CMTime)time
上的AVMutableVideoCompositionLayerInstruction
。
您必须根据图层边界自行形成变换。 它有点尝试和错误,因为它将是一些CGAffineTransformMakeTranslation
和CGAffineTransformMakeScale
组合。 所以对于张贴图片中的案例,你需要放大和翻译。 如果界限出去,那么你必须缩小规模。
斯威夫特 3
let mainComposition = AVMutableVideoComposition()
mainComposition.renderSize = CGSize(width: yourWidth, height: yourHeight)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.