AVCaptureVideoPreviewLayer orientation - need landscape
- soleil
- 2013-02-25 19:53
- 9
My app is landscape only. I'm presenting the AVCaptureVideoPreviewLayer like this:
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 has a frame of (0,0,568,320), which is correct. self.previewLayer logs a frame of (0,0,568,320), which is theoretically correct. However, the camera display appears as a portrait rectangle in the middle of the landscape screen, and the orientation of the camera preview image is wrong by 90 degrees. What am I doing wrong? I need the camera preview layer to appear in the full screen, in landscape mode, and the image should be orientated correctly.
9 Answers
BEST ANSWER FOR SWIFT 3.0 AND 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 } } } }
BEST ANSWER FOR SWIFT 2.2 AND 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 } } } }
avcapturevideoorientation, A Boolean value that indicates whether the connection supports setting the videoFieldMode property. A Boolean value that indicates whether the connection supports setting the videoFieldMode property.
Maselko
2016-10-20 09:40
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 case .landscapeRight: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.landscapeRight case .landscapeLeft: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.landscapeLeft case .portraitUpsideDown: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown default: previewLayerConnection.videoOrientation = AVCaptureVideoOrientation.portrait } } } }
avcapturevideopreviewlayer prevent rotation, On this view controller, I also display the view of another VC at index 0. This view contains a AVCaptureVideoPreviewLayer. I would like my video camera to mimic the Apple video camera app, where the interface layout adjusts with the rotation, but the video preview layer does not. On this view controller, I also display the view of another VC at index 0. This view contains a AVCaptureVideoPreviewLayer. I would like my video camera to mimic the Apple video camera app, where the interface layout adjusts with the rotation, but the video preview layer does not.
Janusz Chudzynski
2019-06-20 07:17
We can't use
[previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
because UIInterfaceOrientation != AVCaptureVideoOrientation
But we can just test values... and this work,with following code.
-(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; } }
avcapturevideodataoutput orientation, Currently, the capture outputs for a movie file (AVCaptureMovieFileOutput) and still image (AVCaptureStillImageOutput) support setting the orientation, but the data output for processing video frames (AVCaptureVideoDataOutput) does not. Currently, the capture outputs for a movie file (AVCaptureMovieFileOutput) and still image (AVCaptureStillImageOutput) support setting the orientation, but the data output for processing video frames (AVCaptureVideoDataOutput) does not.
Emmanuel Crombez
2018-09-10 09:39
The API seems to have changed somewhat. videoOrientation
is now a property on the preview layer's connection
property. Furthermore, no need to use a switch. Answer for 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 connection.videoOrientation = videoOrientation } } }
avcapturephotooutput orientation, AVCapturePhotoOutput() Default constructor, initializes a new instance of this class. AVCapturePhotoOutput(IntPtr) A constructor used when creating managed representations of unmanaged objects; Called by the runtime. AVCapturePhotoOutput(NSObjectFlag) Constructor to call on derived classes to skip initialization and merely allocate the object. AVCapturePhotoOutput() Default constructor, initializes a new instance of this class. AVCapturePhotoOutput(IntPtr) A constructor used when creating managed representations of unmanaged objects; Called by the runtime. AVCapturePhotoOutput(NSObjectFlag) Constructor to call on derived classes to skip initialization and merely allocate the object.
John Rogers
2019-06-03 01:04
For anyone who is struggling on a full functional camera preview. Here is production code. Of course the drawback is a lag when orientation changes. If anyone have better solution to overcome this please share
- (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
avcapturevideopreviewlayer frame, Shop a Wide Selection of Frames Online. Get Free Shipping on Qualified Orders! Shop a Wide Selection of Frames Online. Get Free Shipping on Qualified Orders!
Lance Mao
2014-03-14 01:30
As there is a deprecation and conversion warning using the above solution, and setting videoOrientation didn't seem to be working in iOS7, I put checks for orientation in my getter for the AVCaptureVideoPreviewLayer like so:
- (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; }
swift landscape orientation, Swift 4, Tested in iOS 11 You can specify the orientation in projectTarget -> General -> DeploymentInfo(Device Orientation) -> Portrait (Landscapeleft and Landscaperight are optional) AppDelegate Swift 4, Tested in iOS 11 You can specify the orientation in projectTarget -> General -> DeploymentInfo(Device Orientation) -> Portrait (Landscapeleft and Landscaperight are optional) AppDelegate
David van Dugteren
2013-12-17 22:28
Works with 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 } }
One subtlety to notice here is that UIDeviceOrientation.landscapeRight
goes with AVCaptureVideoOrientation.landscapeLeft
.
The other landscape case is similarly mismatched. This is deliberate, and accommodates an unfortunate mismatch between UIKit and AVFoundation. If you match the cases up by matching the names, it won't work, and your video will be upside down in landscape configurations.
avcapturevideoorientation example, enum AVCaptureVideoOrientation: Int. Overview. You use these constants in conjunction with an AVCapture Video Preview Layer object; see video Orientation. Topics enum AVCaptureVideoOrientation: Int. Overview. You use these constants in conjunction with an AVCapture Video Preview Layer object; see video Orientation. Topics
algal
2017-11-14 05:11
Correct mapping from UIDeviceOrientation
to AVCaptureVideoOrientation
is necessary.
If your app supports device rotation
, resizing preview.frame
is also necessary, and this func
should be called from viewDidLayoutSubviews()
and 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 } } }
avcaptureconnection video orientation, A Boolean value that indicates whether the connection supports changing the orientation of the video. enum AVCapture Video Orientation Constants indicating video orientation. A Boolean value that indicates whether the connection supports changing the orientation of the video. enum AVCapture Video Orientation Constants indicating video orientation.
Water
2018-09-10 09:16
The default camera orientation is Landscape Left (home button one the left). You need to do two things here:
1- Change the previewLayer frame to:
You need to set the preview layer frame to the bounds of the screen so that the frame of the preview layer changes when the screen rotates (you cannot use frame of the root view because that does not change with rotation but the bounds of the root view do). In your example, you are setting the previewlayer frame to a previewView property which I do not see.
2- You need to rotate the preview layer connection with the rotation of the device. Add this code in viewDidAppear:
Hope this solves it.
Full Disclosure: This is a simplified version since you do not care if Landscape right or Landscape left.
orientation, The default camera orientation is Landscape Left (home button one the left). You need to do two things here: 1- Change the previewLayer frame to: self. orientation. The layer's orientation. Deprecated. Use videoOrientation ( AVCaptureConnection ) instead. SDK. iOS 4.0–6.0Deprecated. Framework.
Khaled Barazi
2013-02-25 20:54