AVCaptureVideoPreviewLayer平滑方向旋转

时间:2011-08-10 19:59:05

标签: iphone ios avfoundation

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

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

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

如何让这个图层像Camera相同,同时保持其子视图的旋转?

另外,顺便说一下,我已经看到在AVCaptureVideoPreviewLayer上折旧了orientation属性,而且应该使用AVCaptureConnection的videoOrientation属性,但我不认为我有AVCaptureConnection可以在这里访问(我是只需在屏幕上显示相机)。我该如何设置它以访问videoOrientation?

9 个答案:

答案 0 :(得分:12)

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

- (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中逐步淘汰,并替换为处理设备旋转的新方法,因此现在可以使用,但不是最佳解决方案。

答案 1 :(得分:10)

这是一个老问题,但由于它没有一个公认的答案,而且我在过去的几天里一直在处理这个问题,我想我会发布答案。

根据设备方向更改来旋转视频的技巧是 NOT ROTATE AVCaptureVideoPreviewLayerAVCaptureConnection

更改了AVCaptureConnection上的方向(AVCaptureVideoPreviewLayer的方向已被弃用)总是会导致动画更改。在视频旋转后,您会仍然需要转换预览图层。

执行此操作的正确方法是使用AVAssetWriter编写视频和音频数据..然后在开始录制时对AVAssetWriterInput应用转换。

以下是Apple关于此事的简介 -

Receiving rotated CVPixelBuffers from AVCaptureVideoDataOutput

  

要请求缓冲区轮换,客户端调用-setVideoOrientation:on   AVCaptureVideoDataOutput的视频AVCaptureConnection。注意   物理旋转缓冲区确实带来性能成本,所以只有   如果有必要请求轮换。 例如,如果你想要的话   使用AVAssetWriter旋转写入QuickTime影片文件的视频,   最好在上面设置-transform属性   AVAssetWriterInput而不是物理地旋转缓冲区   AVCaptureVideoDataOutput

变换的应用与上面发布的代码类似。

  1. 您注册了设备方向更改。
  2. 根据新设备方向跟踪要应用的转换。
  3. 按下记录时,设置AVAssetWriterInput并对其应用变换。
  4. 希望这可以帮助那些正在寻找答案的人。

答案 2 :(得分:2)

我能够通过以下代码完成此任务:

需要相机时设置:

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

答案 3 :(得分:1)

我希望AVCaptureVideoPreviewLayer能够完美地跟随所有旋转。这就是我做的方式(overlayView只是一个碰巧有正确边界的视图)。当你将你的电话调到超级精确的代码时,它只会在没有启动的情况下动画:

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

答案 4 :(得分:1)

您应该使用适用于图层的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);

在我的情况下,作为“self.previewLayer”旋转的图层。请记住,之后应将其剪辑到容器视图的边界:

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

。 。

编辑:如果您要通过旋转设备(willAnimateRotationToInterfaceOrientation)进行旋转,则应进行如下转换:

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

这样,图层就会像视图一样旋转。

答案 5 :(得分:0)

我就是这样做的:

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

检查Apple Example之后,我想出了如何在iOS 11及更高版本中禁用videoPreviewLayer的旋转功能。 (不确定是否适用于旧版本)

只需在clipToBounds = false的{​​{1}}上设置UIView

希望这会有所帮助。

答案 8 :(得分:-2)

由于AVCaptureVideoPreviewLayer是CALayer,因此任何更改都将设置动画http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html。要仅停留动画[previewLayer removeAllAnimations]