AVCaptureVideoPreviewLayer平滑方向旋转



我试图禁用任何可识别的方向旋转到AVCaptureVideoPreviewLayer,同时仍然保持任何子视图的旋转。AVCaptureVideoPreviewLayer确实有一个朝向属性,改变它确实允许图层在任何朝向下正确显示。然而,旋转涉及到AVCaptureVideoPreviewLayer的一些时髦的旋转,而不是像在Camera应用程序中那样保持平滑。

这就是我如何使方向正常工作,减去旋转中的障碍:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    _captureVideoPreviewLayer.frame = self.view.bounds;
    _captureVideoPreviewLayer.orientation = [[UIDevice currentDevice] orientation];
}

我如何让这个层像相机应用程序一样,同时保持其子视图的旋转?

另外,作为旁白,我已经看到方向属性在AVCaptureVideoPreviewLayer上贬值,而AVCaptureConnection的videoOrientation属性应该被使用,但我不认为我有一个AVCaptureConnection在这里访问(我只是在屏幕上显示相机)。我应该如何设置这个访问videoOrientation?

对包含预览层的视图使用仿射变换创建平滑过渡:

- (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中已经被淘汰,取而代之的是处理设备旋转的新方法,所以这个功能目前可以工作,但不是最好的解决方案。

这是一个老问题,但由于它没有一个公认的答案,而且我自己在过去的几天里一直在处理这个问题,我想我应该把答案贴出来。

根据设备方向变化旋转视频的技巧是根本不旋转 AVCaptureVideoPreviewLayerAVCaptureConnection

改变AVCaptureConnection的方向(AVCaptureVideoPreviewLayer的方向已被弃用)ALWAYS会导致一个丑陋的动画变化。在视频旋转之后,你仍然需要变换预览层。

正确的方法是使用AVAssetWriter写入视频和音频数据。然后在你开始录制的时候对AVAssetWriterInput应用一个变换。

这是苹果公司关于这个的宣传——

从AVCaptureVideoDataOutput接收旋转CVPixelBuffers

请求缓冲区旋转,客户端调用-setVideoOrientation: onAVCaptureVideoDataOutput的视频AVCaptureConnection。请注意,物理旋转缓冲区确实会带来性能成本,所以只有如果需要,请求轮换。例如,如果您想要旋转视频写入QuickTime电影文件使用avassetwwriter,属性上设置-transform属性更可取AVAssetWriterInput而不是物理旋转缓冲区AVCaptureVideoDataOutput。

转换的应用类似于上面发布的代码。

  1. 你注册设备方向的改变。
  2. 根据新的设备方向跟踪转换应用
  3. 当记录被按下时,设置AVAssetWriterInput并对其应用transform。

我可以用下面的代码完成这个任务:

当你需要相机时设置:

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

我想让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];
}

你应该使用CATransform3DMakeRotation,这意味着图层,就像它已经在评论中提到的:

例如:

float degreesToRotate = 90.0; //Degrees you want to rotate
self.previewLayer.transform = CATransform3DMakeRotation(degreesToRotate / 180.0 * M_PI, 0.0, 0.0, 1.0);

在我的例子中,是"自我"。previewLayer:要旋转的图层。记住,你应该把它剪辑到它的容器视图的边界上:

self.previewLayer.frame = self.view.bounds;

。..

EDIT:如果你要旋转设备(willAnimateRotationToInterfaceOrientation)的结果,你应该做这样的转换:

[CATransaction begin];
    [CATransaction setAnimationDuration:duration];
    [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    //**Perform Transformation here**
    [CATransaction commit];

这样,图层会随着视图旋转

我是这样做的:

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

在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
    }
}

在研究了苹果的例子之后,我已经找到了如何在iOS 11和更高版本中禁用videoPreviewLayer的旋转。(不确定旧版本是否适用)

只需将clipToBounds = false设置在videoPreviewLayerUIView上。

由于AVCaptureVideoPreviewLayer是一个cal_layer,那么任何更改将是动画,http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html。要停止动画,只需点击[previewLayer removeAllAnimations] .

最新更新