ios orientation avfoundation avcapturesession avcapture

ios - AVCaptureVideoPreviewLayer orientación-necesita paisaje



orientation avfoundation (17)

Mi aplicación es sólo de paisaje. Estoy presentando AVCaptureVideoPreviewLayer de esta manera:

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 tiene un marco de (0,0,568,320), que es correcto. self.previewLayer registra un marco de (0,0,568,320), que es teóricamente correcto. Sin embargo, la pantalla de la cámara aparece como un rectángulo vertical en el centro de la pantalla horizontal, y la orientación de la imagen de vista previa de la cámara es incorrecta en 90 grados. ¿Qué estoy haciendo mal? Necesito que la capa de vista previa de la cámara aparezca en pantalla completa, en modo horizontal, y la imagen debe estar orientada correctamente.


MEJOR RESPUESTA PARA SWIFT 3.0 Y 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 } } } }

LA MEJOR RESPUESTA PARA SWIFT 2.2 Y 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 } } } }


Aquí está la solución que estoy usando con Swift 4.

Es corto y funciona muy bien para mí.

open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) let videoLayer = self.previewLayer coordinator.animate(alongsideTransition: { (context: UIViewControllerTransitionCoordinatorContext) in guard let connection = videoLayer?.connection, connection.isVideoOrientationSupported, let orientation = AVCaptureVideoOrientation(rawValue: UIApplication.shared.statusBarOrientation.rawValue) else { return } connection.videoOrientation = orientation videoLayer?.frame = self.view.bounds }) { (context: UIViewControllerTransitionCoordinatorContext) in // handle any completion logic here... } }


Como hay una advertencia de desaprobación y conversión al utilizar la solución anterior, y la configuración de la Orientación de video no parece funcionar en iOS7, puse comprobaciones de orientación en mi procesador para el AVCaptureVideoPreviewLayer así:

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


Funciona con 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 } }

Una sutileza que se debe notar aquí es que UIDeviceOrientation.landscapeRight va con AVCaptureVideoOrientation.landscapeLeft .

El otro caso del paisaje es similar no coincidente. Esto es deliberado y se adapta a un desafortunado desajuste entre UIKit y AVFoundation. Si comparas los casos con los nombres, no funcionará, y tu video estará al revés en configuraciones horizontales.


Hola chicos, gracias por todas las respuestas y comentarios sobre esto, encontré esto mientras estoy trabajando en una aplicación rápida. Puede lograr que la cámara gire con su dispositivo usando este código:

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 }

¡Espero que esto ayude!


La única forma en que me funcionó en iOS 8 a 11.1 sin ningún problema es hacerlo y debo mencionar que en mi caso, cargué mi aplicación solo en modo horizontal pero debería funcionar en todas las orientaciones. (Por cierto, puede superponer el Cámara manualmente a través de imageview o cualquier cosa que quieras de esta manera muy fácilmente)

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


La API parece haber cambiado un poco. videoOrientation ahora es una propiedad en la propiedad de connection la capa de vista previa. Además, no es necesario utilizar un interruptor. Respuesta para 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 } } }


La orientación predeterminada de la cámara es Horizontal izquierda (botón de inicio uno a la izquierda). Necesitas hacer dos cosas aquí:

1- Cambie el marco previewLayer a:

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

Debe establecer el marco de la capa de vista previa en los límites de la pantalla para que el marco de la capa de vista previa cambie cuando la pantalla gire (no puede usar el marco de la vista raíz porque eso no cambia con la rotación sino con los límites de la vista raíz) hacer). En su ejemplo, está configurando el marco de previewlayer a una propiedad previewView que no veo.

2- Es necesario rotar la conexión de la capa de vista previa con la rotación del dispositivo. Agregue este código en 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]]; }

Espero que esto lo resuelva.

Divulgación completa: esta es una versión simplificada, ya que no le importa si Paisaje a la derecha o Paisaje a la izquierda.


La respuesta de @Maselko es correcta, pero por un lado: debe usar UIApplication.shared.statusBarOrientation lugar de UIDevice.current.orientation , porque la orientación del dispositivo es la forma en que su dispositivo está físicamente posicionado. Se rompe cuando su dispositivo está en horizontal, pero su interfaz de usuario no admite esa orientación (como cuando estaba creando una aplicación de cámara solo horizontal e inicia la vista cuando el dispositivo está en posición vertical).

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 = UIApplication.shared.statusBarOrientation 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 } } } }


No podemos usar

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

porque UIInterfaceOrientation != AVCaptureVideoOrientation

Pero solo podemos probar valores ... y este trabajo, con el siguiente código.

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


Para cualquier persona que está luchando en una vista previa de la cámara funcional completa. Aquí está el código de producción. Por supuesto, el inconveniente es un retraso cuando cambia la orientación. Si alguien tiene mejor solución para superar esto, por favor comparta

- (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


Primero necesitamos crear el AVCaptureVideoPreviewLayer y:

  1. configura su videoGravity (como en mi caso, estoy usando una vista pequeña para obtener la salida de video).
  2. establecer el marco.

    [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; [_videoPreviewLayer setFrame:_viewPreview.layer.bounds];

  3. establecer la orientación inicialmente

    if (_videoPreviewLayer.connection.supportsVideoOrientation) { _videoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:[UIApplication sharedApplication].statusBarOrientation]; }

  4. establece la orientación para cada caso usando un simple interruptor de caja

    -(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. Dado que el dispositivo es compatible con landscapeLeft y landscapeRight, use el delegado llamado en rotación:

    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ if (_videoPreviewLayer.connection.supportsVideoOrientation) { _videoPreviewLayer.connection.videoOrientation = [self interfaceOrientationToVideoOrientation:toInterfaceOrientation]; } }


Respuesta seleccionada trabajando para Swift 4.2 - Xcode 10.0 - iOS 12.0:

var videoPreviewLayer: AVCaptureVideoPreviewLayer? override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let previewLayerConnection = self.videoPreviewLayer?.connection, previewLayerConnection.isVideoOrientationSupported { updatePreviewLayer(layer: previewLayerConnection, orientation: UIApplication.shared.statusBarOrientation.videoOrientation) } } private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) { layer.videoOrientation = orientation videoPreviewLayer?.frame = self.view.bounds }

No olvide la asignación de UIInterfaceOrientation a AVCaptureVideoOrientation

extension UIInterfaceOrientation { public var videoOrientation: AVCaptureVideoOrientation { switch self { case .portrait: return AVCaptureVideoOrientation.portrait case .landscapeRight: return AVCaptureVideoOrientation.landscapeRight case .landscapeLeft: return AVCaptureVideoOrientation.landscapeLeft case .portraitUpsideDown: return AVCaptureVideoOrientation.portraitUpsideDown default: return AVCaptureVideoOrientation.portrait } } }


Yo también estaba frente a la misma Esto era para arreglar la orientación de mi cámara

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

Para arreglar la camara

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


La asignación correcta de UIDeviceOrientation a AVCaptureVideoOrientation es necesaria.

Si su aplicación admite la device rotation , también es necesario resizing preview.frame , y esta func debe llamarse desde viewDidLayoutSubviews() y viewWillTransition() .

private func configureVideoOrientation() { if let preview = self.previewLayer, let connection = preview.connection { let orientation = UIDevice.current.orientation if connection.isVideoOrientationSupported { var videoOrientation: AVCaptureVideoOrientation switch orientation { case .portrait: videoOrientation = .portrait case .portraitUpsideDown: videoOrientation = .portraitUpsideDown case .landscapeLeft: videoOrientation = .landscapeRight case .landscapeRight: videoOrientation = .landscapeLeft default: videoOrientation = .portrait } preview.frame = self.view.bounds connection.videoOrientation = videoOrientation } } }


La respuesta de Maselko casi funcionó para mí, excepto que si la orientación de la barra de estado cambia de posición, la salida de la cámara se muestra al revés. He abordado ese problema volviendo a llamar a la lógica de Maselko cuando la barra de estado se invierte.

Aquí está mi solución de Maselko modificada (probada en ios12 / swift4):

override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() setCameraOrientation() } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) setCameraOrientation() } @objc func setCameraOrientation() { if let connection = self.previewLayer?.connection { let currentDevice: UIDevice = UIDevice.current let orientation: UIDeviceOrientation = currentDevice.orientation let previewLayerConnection : AVCaptureConnection = connection if previewLayerConnection.isVideoOrientationSupported { let o: AVCaptureVideoOrientation switch (orientation) { case .portrait: o = .portrait case .landscapeRight: o = .landscapeLeft case .landscapeLeft: o = .landscapeRight case .portraitUpsideDown: o = .portraitUpsideDown default: o = .portrait } previewLayerConnection.videoOrientation = o previewLayer!.frame = self.view.bounds } } }


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