AVCaptureVideoPreviewLayer 平滑方向旋转
Posted
技术标签:
【中文标题】AVCaptureVideoPreviewLayer 平滑方向旋转【英文标题】:AVCaptureVideoPreviewLayer smooth orientation rotation 【发布时间】:2011-08-10 19:59:04 【问题描述】:我正在尝试禁用任何可识别的方向旋转到 AVCaptureVideoPreviewLayer,同时仍保持任何子视图的旋转。 AVCaptureVideoPreviewLayer 确实有一个方向属性,并且更改它确实允许图层正确显示任何方向。但是,旋转涉及 AVCaptureVideoPreviewLayer 的一些时髦旋转,而不是像在 Camera 应用中那样保持平滑。
这就是我如何让方向正常工作,减去旋转中的障碍:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
_captureVideoPreviewLayer.frame = self.view.bounds;
_captureVideoPreviewLayer.orientation = [[UIDevice currentDevice] orientation];
如何让这个层像相机应用一样工作,同时保持其子视图的旋转?
另外,顺便说一句,我已经看到 AVCaptureVideoPreviewLayer 上的方向属性已被贬值,应该使用 AVCaptureConnection 的 videoOrientation 属性,但我认为我没有 AVCaptureConnection 可以访问这里(我是只需在屏幕上显示相机)。我应该如何设置它以访问 videoOrientation?
【问题讨论】:
【参考方案1】:在包含预览层的视图上使用仿射变换可创建平滑过渡:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
if (cameraPreview)
if (toInterfaceOrientation==UIInterfaceOrientationPortrait)
cameraPreview.transform = CGAffineTransformMakeRotation(0);
else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeLeft)
cameraPreview.transform = CGAffineTransformMakeRotation(M_PI/2);
else if (toInterfaceOrientation==UIInterfaceOrientationLandscapeRight)
cameraPreview.transform = CGAffineTransformMakeRotation(-M_PI/2);
cameraPreview.frame = self.view.bounds;
willAnimateRotationToInterfaceOrientation
函数在旋转动画块中调用,因此此处更改属性将与其余的旋转动画一起动画。此功能已在 ios 6 中逐步淘汰,取而代之的是处理设备旋转的新方法,因此目前可行,但不是最佳解决方案。
【讨论】:
Xcode 告诉我,我不能使用 CGAffineTransformMakeRotation 方法,而对于我的预览层,我需要使用 CATransform3DMakeRotation 方法。你的代码是错的还是我做错了什么? @Will CGAffineTransformMakeRotation 用于 UIViews,CATransform3DMakeRotation 用于 CGLayerswillAnimateRotationToInterfaceOrientation:duration:
现在已弃用。通过在[coordinator animateAlongsideTransition:completion:
的动画块内分配变换/帧,您可以使用viewWillTransitionToSize:withTransitionCoordinator:
获得相同的结果。【参考方案2】:
这是一个老问题,但由于它没有公认的答案,而且过去几天我自己一直在处理这个问题,我想我会发布答案。
根据设备方向变化旋转视频的技巧是不旋转AVCaptureVideoPreviewLayer
或AVCaptureConnection
。
更改AVCaptureConnection
上的方向(AVCaptureVideoPreviewLayer
的方向已弃用)总是会导致丑陋的动画变化.. 旋转视频后,您仍然需要转换预览层。
执行此操作的正确方法是使用AVAssetWriter
写入视频和音频数据......然后在开始录制时对AVAssetWriterInput
应用转换。
这是来自 Apple 的关于此的简介 --
Receiving rotated CVPixelBuffers from AVCaptureVideoDataOutput
为了请求缓冲区旋转,客户端调用 -setVideoOrientation: on AVCaptureVideoDataOutput 的视频 AVCaptureConnection。注意 物理旋转缓冲区确实会带来性能成本,所以只有 必要时请求轮换。 例如,如果您想要 使用 AVAssetWriter 将旋转视频写入 QuickTime 电影文件, 最好在 AVAssetWriterInput 而不是物理旋转缓冲区 AVCaptureVideoDataOutput。
转换的应用类似于上面发布的代码。
-
您注册了设备方向更改。
根据新的设备方向跟踪要应用的变换。
按下记录时,设置 AVAssetWriterInput 并对其应用转换。
希望这对正在寻找答案的人有所帮助。
【讨论】:
在使用“setVideoOrientation”时遇到类似问题,因此感谢 Kal 的评论。既然我知道了这个问题,那么是否有任何例子可以证明这一点?顺便说一句,引用的链接不再有效。试试这个链接:developer.apple.com/library/Mac/releasenotes/AudioVideo/… 找到了一个例子,不出所料,它也是来自 Kal。这是链接:runkalrun.blogspot.com/2013/01/…。谢谢 Kal,很棒的信息!【参考方案3】:我能够使用以下代码完成此操作:
在需要相机时进行设置:
preview = [[self videoPreviewWithFrame:CGRectMake(0, 0, 320, 480)] retain];
[self.view addSubview:preview];
videoPreviewWithFrame
函数。
- (UIView *) videoPreviewWithFrame:(CGRect) frame
AVCaptureVideoPreviewLayer *tempPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]];
[tempPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
tempPreviewLayer.frame = frame;
UIView* tempView = [[UIView alloc] init];
[tempView.layer addSublayer:tempPreviewLayer];
tempView.frame = frame;
[tempPreviewLayer autorelease];
[tempView autorelease];
return tempView;
【讨论】:
【参考方案4】:我希望 AVCaptureVideoPreviewLayer 能够很好地跟随所有旋转。我就是这样做的(overlayView
只是一个恰好具有正确边界的视图)。只有当您将调用 super 准确地放在代码中的位置时,它才会在没有打断的情况下进行动画处理:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
if ([[[self previewLayer] connection] isVideoOrientationSupported])
[[[self previewLayer] connection] setVideoOrientation:toInterfaceOrientation];
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
CGRect layerRect = [[self overlayView] bounds];
[[self previewLayer] setBounds:layerRect];
[[self previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),
CGRectGetMidY(layerRect))];
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration: duration];
【讨论】:
缺点:对我来说,这个解决方案还不够。我从这个相机预览中制作的快照在加载到 UIImageView 时,处于相机预览的原始方向。我会为此寻找一个单独的解决方案,也许会提出一个新问题...... 下一个缺点:老板说这不够帅。所以我们像他们一样做,修复纵向的相机预览并旋转控件以监听 UIDeviceOrientation 的变化......【参考方案5】:您应该使用CATransform3DMakeRotation
,它用于层,就像在 cmets 中提到的那样:
例如:
float degreesToRotate = 90.0; //Degrees you want to rotate
self.previewLayer.transform = CATransform3DMakeRotation(degreesToRotate / 180.0 * M_PI, 0.0, 0.0, 1.0);
在我的例子中,“self.previewLayer”是要旋转的图层。请记住,之后您应该将其剪辑到它的容器视图的边界:
self.previewLayer.frame = self.view.bounds;
。 . .
编辑:如果您要因设备旋转而旋转 (willAnimateRotationToInterfaceOrientation
),您应该进行如下转换:
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
//**Perform Transformation here**
[CATransaction commit];
这样,图层将像视图一样旋转。
【讨论】:
【参考方案6】:这就是我的做法:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
if (tookFirstPicture)
return;
if (toInterfaceOrientation == UIInterfaceOrientationPortrait)
[previewLayer setOrientation:AVCaptureVideoOrientationPortrait];
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft)
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft];
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
[previewLayer setOrientation:AVCaptureVideoOrientationLandscapeRight];
else
[previewLayer setOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
【讨论】:
这在 iOS 6 中已被弃用。使用AVCaptureConnection's -setVideoOrientation:
【参考方案7】:
我在 swift 中使用:
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval)
if !UIDevice.currentDevice().model.contains("Simulator")
if (toInterfaceOrientation == .Portrait)
prevLayer?.transform = CATransform3DMakeRotation(0, 0, 0, 1)
else if (toInterfaceOrientation == .LandscapeLeft)
prevLayer?.transform = CATransform3DMakeRotation(CGFloat(M_PI)/2, 0, 0, 1)
else if (toInterfaceOrientation == .LandscapeRight)
prevLayer?.transform = CATransform3DMakeRotation(-CGFloat(M_PI)/2, 0, 0, 1)
prevLayer?.frame = self.scanView.frame
【讨论】:
willAnimateRotationToInterfaceOrientation 在 iOS8 中已被弃用 您能提供一个替代解决方案吗? 看看 Kal 的回答。最好的解决方案是不要旋转它。【参考方案8】:在检查了Apple Example 之后,我想出了如何在 iOS 11 及更高版本中禁用 videoPreviewLayer
的旋转。 (不确定旧版本是否同样适用)
只需在videoPreviewLayer
的UIView
上设置clipToBounds = false
。
希望这会有所帮助。
【讨论】:
【参考方案9】:由于AVCaptureVideoPreviewLayer
是一个CALayer,所以任何更改都会被动画化,http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html。要停止动画,只需[previewLayer removeAllAnimations]
。
【讨论】:
以上是关于AVCaptureVideoPreviewLayer 平滑方向旋转的主要内容,如果未能解决你的问题,请参考以下文章