【问题标题】:AVCaptureVideoPreviewLayer smooth orientation rotationAVCaptureVideoPreviewLayer 平滑方向旋转
【发布时间】: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?

【问题讨论】:

    标签: iphone ios avfoundation


    【解决方案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 用于 CGLayers
    • willAnimateRotationToInterfaceOrientation:duration: 现在已弃用。通过在[coordinator animateAlongsideTransition:completion: 的动画块内分配变换/帧,您可以使用viewWillTransitionToSize:withTransitionCoordinator: 获得相同的结果。
    【解决方案2】:

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

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

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

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

    这是来自 Apple 的关于此的简介 --

    Receiving rotated CVPixelBuffers from AVCaptureVideoDataOutput

    为了请求缓冲区旋转,客户端调用 -setVideoOrientation: on AVCaptureVideoDataOutput 的视频 AVCaptureConnection。注意 物理旋转缓冲区确实会带来性能成本,所以只有 必要时请求轮换。 例如,如果您想要 使用 AVAssetWriter 将旋转视频写入 QuickTime 电影文件, 最好在 AVAssetWriterInput 而不是物理旋转缓冲区 AVCaptureVideoDataOutput。

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

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

    希望这对正在寻找答案的人有所帮助。

    【讨论】:

    【解决方案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 的旋转。 (不确定旧版本是否同样适用)

        只需在videoPreviewLayerUIView 上设置clipToBounds = false

        希望这会有所帮助。

        【讨论】:

          【解决方案9】:

          由于AVCaptureVideoPreviewLayer 是一个CALayer,所以任何更改都会被动画化,http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/AnimatingLayers.html。要停止动画,只需[previewLayer removeAllAnimations]

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-09-25
            • 2013-12-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多