ios AVCaptureVideoPreviewLayer 方向 - 需要横向
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15075300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AVCaptureVideoPreviewLayer orientation - need landscape
提问by soleil
My app is landscape only. I'm presenting the AVCaptureVideoPreviewLayer like this:
我的应用程序仅限横向。我正在呈现这样的 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 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.
self.previewView 有一个 (0,0,568,320) 的框架,这是正确的。self.previewLayer 记录一帧 (0,0,568,320),理论上是正确的。但是,相机显示在横向屏幕中间显示为纵向矩形,并且相机预览图像的方向错误90度。我究竟做错了什么?我需要相机预览层以全屏、横向模式显示,并且图像的方向应该正确。
回答by Maselko
BEST ANSWER FOR SWIFT 3.0 AND XCODE 8.0
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
}
}
}
}
BEST ANSWER FOR SWIFT 2.2 AND XCODE 7.3
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
}
}
}
}
回答by Khaled Barazi
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:
1-将 previewLayer 框架更改为:
self.previewLayer.frame=self.view.bounds;
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.
您需要将预览图层框架设置为屏幕的边界,以便在屏幕旋转时预览图层的框架会发生变化(您不能使用根视图的框架,因为它不会随着旋转而变化,而是根视图的边界做)。在您的示例中,您将 previewlayer 框架设置为我看不到的 previewView 属性。
2- You need to rotate the preview layer connection with the rotation of the device. Add this code in viewDidAppear:
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]];
}
Hope this solves it.
希望这能解决它。
Full Disclosure: This is a simplified version since you do not care if Landscape right or Landscape left.
完全披露:这是一个简化版本,因为您不关心横向是右还是横向。
回答by Janusz Chudzynski
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
}
}
}
}
回答by Emmanuel Crombez
We can't use
我们不能用
[previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
because UIInterfaceOrientation != AVCaptureVideoOrientation
因为 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;
}
}
回答by John Rogers
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:
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
connection.videoOrientation = videoOrientation
}
}
}
回答by Lance Mao
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
回答by David van Dugteren
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:
由于使用上述解决方案存在弃用和转换警告,并且设置 videoOrientation 在 iOS7 中似乎不起作用,因此我在 AVCaptureVideoPreviewLayer 的 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;
}
回答by algal
Works with Swift 4, Xcode 9:
适用于 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
.
这里要注意的一个微妙之处是UIDeviceOrientation.landscapeRight
与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.
另一个景观案例同样不匹配。这是故意的,并适应了 UIKit 和 AVFoundation 之间不幸的不匹配。如果您通过匹配名称来匹配案例,它将不起作用,并且您的视频将在横向配置中颠倒。
回答by juliancadi
Selected answer working for Swift 4.2 - Xcode 10.0 - iOS 12.0:
适用于 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
}
Don't forget the mapping from UIInterfaceOrientation to AVCaptureVideoOrientation
不要忘记从 UIInterfaceOrientation 到 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
}
}
}
回答by Water
Correct mapping from UIDeviceOrientation
to AVCaptureVideoOrientation
is necessary.
需要从UIDeviceOrientation
到的正确映射AVCaptureVideoOrientation
。
If your app supports device rotation
, resizing preview.frame
is also necessary, and this func
should be called from viewDidLayoutSubviews()
and viewWillTransition()
.
如果您的应用程序支持device rotation
,resizing preview.frame
也是必要的,这func
应该从viewDidLayoutSubviews()
和调用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
}
}
}