AVCaptureVideoPreviewLayer方向 – 需要景观

我的应用程序只是风景。 我正在像这样呈现AVCaptureVideoPreviewLayer:

self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; [self.previewLayer setBackgroundColor:[[UIColor blackColor] CGColor]]; [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect]; NSLog(@"previewView: %@", self.previewView); CALayer *rootLayer = [self.previewView layer]; [rootLayer setMasksToBounds:YES]; [self.previewLayer setFrame:[rootLayer bounds]]; NSLog(@"previewlayer: %f, %f, %f, %f", self.previewLayer.frame.origin.x, self.previewLayer.frame.origin.y, self.previewLayer.frame.size.width, self.previewLayer.frame.size.height); [rootLayer addSublayer:self.previewLayer]; [session startRunning]; 

self.previewView有一个(0,0,568,320)的框架,这是正确的。 self.previewLayerlogging一个(0,0,568,320)的框架,这在理论上是正确的。 但是,相机显示在横屏中间显示为纵向矩形,相机预览图像的方向错误90度。 我究竟做错了什么? 我需要将相机预览图层以横向模式显示在全屏幕中,并且图像应该正确定位。

默认的摄像机方向是“左侧风景”(左侧的主页button)。 你需要在这里做两件事:

1-将previewLayer框架更改为:

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

您需要将预览图层框设置为屏幕的边界,以便在屏幕旋转时预览图层的框架发生更改(不能使用根视图的框架,因为它不随旋转而改变,而是根视图的边界做)。 在你的例子中,你将预览图层框架设置为我没有看到的previewView属性。

2-您需要旋转设备的旋转预览图层连接。 在viewDidAppear中添加下面的代码:

 -(void) viewDidAppear:(BOOL)animated { [super viewDidAppear:YES]; //Get Preview Layer connection AVCaptureConnection *previewLayerConnection=self.previewLayer.connection; if ([previewLayerConnection isVideoOrientationSupported]) [previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; } 

希望这能解决它。

完全披露:这是一个简化的版本,因为你不关心风景权利或景观离开。

SWIFT 3.0和XCODE 8.0的最佳答案

 private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) { layer.videoOrientation = orientation previewLayer.frame = self.view.bounds } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let connection = self.previewLayer?.connection { let currentDevice: UIDevice = UIDevice.current let orientation: UIDeviceOrientation = currentDevice.orientation let previewLayerConnection : AVCaptureConnection = connection if previewLayerConnection.isVideoOrientationSupported { switch (orientation) { case .portrait: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait) break case .landscapeRight: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft) break case .landscapeLeft: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight) break case .portraitUpsideDown: updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown) break default: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait) break } } } } 

SWIFT 2.2和XCODE 7.3的最佳答案

 private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) { layer.videoOrientation = orientation previewLayer.frame = self.view.bounds } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let connection = self.previewLayer?.connection { let currentDevice: UIDevice = UIDevice.currentDevice() let orientation: UIDeviceOrientation = currentDevice.orientation let previewLayerConnection : AVCaptureConnection = connection if (previewLayerConnection.supportsVideoOrientation) { switch (orientation) { case .Portrait: updatePreviewLayer(previewLayerConnection, orientation: .Portrait) break case .LandscapeRight: updatePreviewLayer(previewLayerConnection, orientation: .LandscapeLeft) break case .LandscapeLeft: updatePreviewLayer(previewLayerConnection, orientation: .LandscapeRight) break case .PortraitUpsideDown: updatePreviewLayer(previewLayerConnection, orientation: .PortraitUpsideDown) break default: updatePreviewLayer(previewLayerConnection, orientation: .Portrait) break } } } } 
 override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator) if let connection = self.previewLayer?.connection { var currentDevice: UIDevice = UIDevice.currentDevice() var orientation: UIDeviceOrientation = currentDevice.orientation var previewLayerConnection : AVCaptureConnection = connection if (previewLayerConnection.supportsVideoOrientation) { switch (orientation) { case .portrait: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portrait break case .landscapeRight: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.landscapeRight break case .landscapeLeft: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.landscapeLeft break case .portraitUpsideDown: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown break default: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portrait break } } } } 

我们不能使用

 [previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; 

因为UIInterfaceOrientation!= AVCaptureVideoOrientation

但是我们可以testing一下价值……这个工作。

 -(void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; switch (orientation) { case UIInterfaceOrientationPortrait: [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait]; break; case UIInterfaceOrientationPortraitUpsideDown: [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown]; break; case UIInterfaceOrientationLandscapeLeft: [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft]; break; case UIInterfaceOrientationLandscapeRight: [_videoPreviewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight]; break; } } 

对于那些在全function相机预览中挣扎的人。 这里是生产代码。 当然,当方向改变时,缺点是滞后。 如果有人有更好的解决scheme来克服这个请分享

 - (void)viewDidLoad { [super viewDidLoad]; [self initCamera]; } - (void)initCamera { AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil]; if (captureInput) { mSession = [[AVCaptureSession alloc] init]; [mSession addInput:captureInput]; } } - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; if ([mSession isRunning]) { [mSession stopRunning]; [mCameraLayer removeFromSuperlayer]; [self initCamera]; [self startCamera]; } } - (void)startCamera { [mSession startRunning]; Settings::getInstance()->setClearColor(Color(0, 0, 0, 0)); mCameraLayer = [AVCaptureVideoPreviewLayer layerWithSession: mSession]; [self updateCameraLayer]; [mCameraView.layer addSublayer:mCameraLayer]; } - (void)stopCamera { [mSession stopRunning]; [mCameraLayer removeFromSuperlayer]; Settings::getInstance()->setDefClearColor(); } - (void)toggleCamera { mSession.isRunning ? [self stopCamera] : [self startCamera]; [mGLKView setNeedsDisplay]; } - (void)updateCameraLayer { mCameraLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; mCameraLayer.frame = mCameraView.bounds; float x = mCameraView.frame.origin.x; float y = mCameraView.frame.origin.y; float w = mCameraView.frame.size.width; float h = mCameraView.frame.size.height; CATransform3D transform = CATransform3DIdentity; if (UIDeviceOrientationLandscapeLeft == [[UIDevice currentDevice] orientation]) { mCameraLayer.frame = CGRectMake(x, y, h, w); transform = CATransform3DTranslate(transform, (w - h) / 2, (h - w) / 2, 0); transform = CATransform3DRotate(transform, -M_PI/2, 0, 0, 1); } else if (UIDeviceOrientationLandscapeRight == [[UIDevice currentDevice] orientation]) { mCameraLayer.frame = CGRectMake(x, y, h, w); transform = CATransform3DTranslate(transform, (w - h) / 2, (h - w) / 2, 0); transform = CATransform3DRotate(transform, M_PI/2, 0, 0, 1); } else if (UIDeviceOrientationPortraitUpsideDown == [[UIDevice currentDevice] orientation]) { mCameraLayer.frame = mCameraView.bounds; transform = CATransform3DMakeRotation(M_PI, 0, 0, 1); } else { mCameraLayer.frame = mCameraView.bounds; } mCameraLayer.transform = transform; } enter code here 

由于使用上述解决scheme存在弃用和转换警告,并且设置videoOrientation在iOS7中似乎不起作用,所以我在AVCptureVideoPreviewLayer的getter中对方向进行检查,如下所示:

 - (AVCaptureVideoPreviewLayer *) previewLayer { if(!_previewLayer) { _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession: self.captureSession]; [_previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; _previewLayer.frame = self.view.bounds; // Assume you want the preview layer to fill the view. [_previewLayer setPosition:CGPointMake(0,0)]; if (UIDeviceOrientationLandscapeLeft == [[UIDevice currentDevice] orientation]) { _previewLayer.transform = CATransform3DMakeRotation(-M_PI/2, 0, 0, 1); } else if (UIDeviceOrientationLandscapeRight == [[UIDevice currentDevice] orientation]) { _previewLayer.transform = CATransform3DMakeRotation(M_PI/2, 0, 0, 1); } } return _previewLayer; } 

API似乎有所改变。 videoOrientation现在是预览图层的connection属性的属性。 而且,不需要使用开关。 回答Swift 3.0:

 override func viewDidLayoutSubviews() { self.configureVideoOrientation() } private func configureVideoOrientation() { if let previewLayer = self.previewLayer, let connection = previewLayer.connection { let orientation = UIDevice.current.orientation if connection.isVideoOrientationSupported, let videoOrientation = AVCaptureVideoOrientation(rawValue: orientation.rawValue) { previewLayer.frame = self.view.bounds previewLayer.connection.videoOrientation = videoOrientation } } } 

嘿,伙计们,感谢所有的答案和反馈,我遇到了这个,因为我正在快速的应用程序工作。 您可以使用以下代码实现相机与您的设备一起旋转:

 override func shouldAutorotate() -> Bool { if your cameraController.previewLayer.connection != nil { var currentDevice: UIDevice = UIDevice.currentDevice() var orientation: UIDeviceOrientation = currentDevice.orientation var previewLayerConnection : AVCaptureConnection = your cameraController.previewLayer.connection if (previewLayerConnection.supportsVideoOrientation) { switch (orientation) { case .Portrait: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait break case .LandscapeRight: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft break case .LandscapeLeft: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight break default: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.Portrait break } } } return true } 

希望这可以帮助!

首先,我们需要创buildAVCaptureVideoPreviewLayer,并且:

  1. 设置其video重力(在我的情况下,使用小视图来获得video输出)。
  2. 设置框架。

     [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; [_videoPreviewLayer setFrame:_viewPreview.layer.bounds]; 
  3. 最初设置方向

     if (_videoPreviewLayer.connection.supportsVideoOrientation) { _videoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation]; } 
  4. 使用简单的开关盒设置每个案例的方向

     -(AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation: 

    -(AVCaptureVideoOrientation)interfaceOrientationToVideoOrientation:

     (UIInterfaceOrientation)orientation { switch (orientation) { case UIInterfaceOrientationPortrait: return AVCaptureVideoOrientationPortrait; case UIInterfaceOrientationPortraitUpsideDown: return AVCaptureVideoOrientationPortraitUpsideDown; case UIInterfaceOrientationLandscapeLeft: return AVCaptureVideoOrientationLandscapeLeft ; case UIInterfaceOrientationLandscapeRight: return AVCaptureVideoOrientationLandscapeRight; default: break; } NSLog(@"Warning - Didn't recognise interface orientation (%d)",orientation); return AVCaptureVideoOrientationPortrait; 

    }

  5. 由于设备同时支持landscapeLeft和landscapeRight,因此使用循环调用的委托:
     - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ if (_videoPreviewLayer.connection.supportsVideoOrientation) { _videoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:toInterfaceOrientation]; } } 

我也面对相同这是为了解决我的相机方向

 override func shouldAutorotate() -> Bool { return false } override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation { return UIInterfaceOrientation.LandscapeLeft } override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { return UIInterfaceOrientationMask.LandscapeLeft } 

修理相机

 let previewLayer: AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.avCaptureSession) previewLayer.frame = self.view.layer.frame previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill 

在iOS 8到11.1中唯一没有任何问题的方法是这样做,我应该提到,在我的情况下,我只在横向模式下加载我的应用程序,但它应该在所有方向上工作(顺便说一句,你可以覆盖相机手动通过ImageView或任何你想要这种方式很容易)

 @interface ViewController (){ AVCaptureVideoPreviewLayer * previewLayer; AVCaptureSession* session; } @property (weak, nonatomic) IBOutlet UIView *cameraPreviewView; -(void)viewDidLoad{ AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil]; if (captureInput) { session = [[AVCaptureSession alloc] init]; [session addInput:captureInput]; } previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; [previewLayer setBackgroundColor:[[UIColor blackColor] CGColor]]; [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect]; CALayer *rootLayer = [self.cameraPreviewView layer]; [rootLayer setMasksToBounds:YES]; [previewLayer setFrame:[self.view bounds]]; [rootLayer addSublayer:previewLayer]; [session startRunning]; //Orientation Code is in viewDidLayoutSubviews method } -(void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; switch (orientation) { case UIInterfaceOrientationPortrait: [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait]; break; case UIInterfaceOrientationPortraitUpsideDown: [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown]; break; case UIInterfaceOrientationLandscapeLeft: [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft]; break; case UIInterfaceOrientationLandscapeRight: [previewLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight]; break; default:break; } } 

适用于Swift 4,Xcode 9:

  override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) guard let conn = self.previewLayer?.connection, conn.isVideoOrientationSupported else { return } let deviceOrientation = UIDevice.current.orientation switch deviceOrientation { case .portrait: conn.videoOrientation = .portrait case .landscapeRight: conn.videoOrientation = .landscapeLeft case .landscapeLeft: conn.videoOrientation = .landscapeRight case .portraitUpsideDown: conn.videoOrientation = .portraitUpsideDown default: conn.videoOrientation = .portrait } } 

这里需要注意的一点是UIDeviceOrientation.landscapeRightAVCaptureVideoOrientation.landscapeLeft一起使用。

另一个景观案例是类似的不匹配。 这是故意的,并适应UIKit和AVFoundation之间不幸的不匹配。 如果您通过匹配名称匹配案例,则无法正常工作,并且您的video在横向configuration中会颠倒。