AVCaptureVideoPreviewLayer方向 - 需要景观

sol*_*eil 56 avfoundation orientation ios avcapture avcapturesession

我的应用只是风景.我将这样呈现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];
Run Code Online (Sandbox Code Playgroud)

self.previewView的框架为(0,0,568,320),这是正确的.self.previewLayer记录一个(0,0,568,320)的帧,这在理论上是正确的.但是,相机显示在横向屏幕中间显示为纵向矩形,并且相机预览图像的方向错误90度.我究竟做错了什么?我需要相机预览层出现在全屏幕,在横向模式下,图像应正确导向.

Mas*_*lko 65

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您从一种横向模式快速旋转到另一种横向模式,此答案将失败.在该场景中不会调用布局方法.我还测试了viewWillLayout并获得了相同的结果. (4认同)
  • 在开关中,2个案例(LandscapeRight和LandscapeLeft)被反转,但效果很好. (4认同)
  • 这非常美丽. (2认同)
  • 你不必在swift的开关内写一个break,就像C一样. (2认同)

Kha*_*azi 64

默认摄像机方向为横向左侧(左侧是主页按钮).你需要在这里做两件事:

1-将previewLayer框架更改为:

self.previewLayer.frame=self.view.bounds;
Run Code Online (Sandbox Code Playgroud)

您需要将预览图层框架设置为屏幕边界,以便在屏幕旋转时预览图层的框架发生更改(您不能使用根视图的框架,因为它不会随着旋转而改变,而是根视图的边界做).在您的示例中,您将预览层框架设置为我看不到的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]]; 
}
Run Code Online (Sandbox Code Playgroud)

希望这能解决它.

完全披露:这是一个简化版本,因为您不关心Landscape Right还是Landscape.


Jan*_*ski 27

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 所有这些"break"语句都是不必要的.Swift`witch`情况默认情况下不会出现,就像在Objective-C中一样.此外,`previewLayerConnection.videoOrientation`被称为`AVCaptureVideoOrientation`,因此您可以将语句简化为`previewLayerConnection.videoOrientation = .portrait`. (3认同)

小智 16

我们不能用

[previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; 
Run Code Online (Sandbox Code Playgroud)

因为 UIInterfaceOrientation != AVCaptureVideoOrientation

但是我们可以通过以下代码测试值...并且这项工作.

-(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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ers 14

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是答案。谢谢! (2认同)

Lan*_*Mao 7

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

- (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
Run Code Online (Sandbox Code Playgroud)