【问题标题】:AVLayerVideoGravityResize does not match on new devices, iOS 10?AVLayerVideoGravityResize 在新设备上不匹配,iOS 10?
【发布时间】:2016-10-03 22:01:59
【问题描述】:

具有全屏实时预览的相机,

    previewLayer!.videoGravity = AVLayerVideoGravityResize

制作图片...

    stillImageOutput?.captureStillImageAsynchronously(
        from: videoConnection, completionHandler:

全屏实时预览将或应该与静止图像精确匹配。

(为了清楚起见:假设您不小心使用了AVLayerVideoGravityResizeAspectFill。在这种情况下,实时预览将与静止图像不匹配 - 您会在拉伸时看到“跳跃”。)

但是...

如果您在 iOS10 上尝试以下操作(因此使用 AVLayerVideoGravityResize - 正确的选择)...

它并不完全有效:您会在实时预览和静止图像之间获得一个小跳跃。一个或另一个被轻微拉伸不正确。

这实际上可能只是某些设备的错误吗?还是在 iOS 中?

(在旧设备上完美运行 - 无需跳转,如果您在 iOS9 上尝试。)

还有其他人看过吗?

// CameraPlane ... the actual live camera plane per se


import UIKit
import AVFoundation

class CameraPlane:UIViewController
    {
    var captureSession: AVCaptureSession?
    var stillImageOutput: AVCaptureStillImageOutput?
    var previewLayer: AVCaptureVideoPreviewLayer?
    
    fileprivate func fixConnectionOrientation()
        {
        if let connection =  self.previewLayer?.connection 
            {
            let previewLayerConnection : AVCaptureConnection = connection
            
            guard previewLayerConnection.isVideoOrientationSupported else
                {
                print("strangely no orientation support")
                return
                }
            
            previewLayerConnection.videoOrientation = neededVideoOrientation()
            previewLayer!.frame = view.bounds
            }
        }
    
    func neededVideoOrientation()->(AVCaptureVideoOrientation)
        {
        let currentDevice:UIDevice = UIDevice.current
        let orientation: UIDeviceOrientation = currentDevice.orientation
        var r:AVCaptureVideoOrientation
        switch (orientation)
            {
            case .portrait: r = .portrait
                break
            case .landscapeRight: r = .landscapeLeft
                break
            case .landscapeLeft: r = .landscapeRight
                break
            case .portraitUpsideDown: r = .portraitUpsideDown
                break
            default: r = .portrait
                break
            }
        return r
        }
    
    override func viewDidLayoutSubviews()
        {
        super.viewDidLayoutSubviews()
        fixConnectionOrientation()
        }
    
    func cameraBegin()
        {
        captureSession = AVCaptureSession()
        
        captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
        // remember that of course, none of this will work on a simulator, only on a device
        
        let backCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
        
        var error: NSError?
        var input: AVCaptureDeviceInput!
        do {
            input = try AVCaptureDeviceInput(device: backCamera)
            } catch let error1 as NSError
                {
                error = error1
                input = nil
                }
        
        if ( error != nil )
            {
            print("probably on simulator? no camera?")
            return;
            }
        
        if ( captureSession!.canAddInput(input) == false )
            {
            print("capture session problem?")
            return;
            }
        
        captureSession!.addInput(input)
        
        stillImageOutput = AVCaptureStillImageOutput()
        stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
        
        if ( captureSession!.canAddOutput(stillImageOutput) == false )
            {
            print("capture session with stillImageOutput problem?")
            return;
            }
        
        captureSession!.addOutput(stillImageOutput)
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        
        // previewLayer!.videoGravity = AVLayerVideoGravityResizeAspect
        // means, won't reach the top and bottom on devices, gray bars
        
        // previewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
        // means, you get the "large squeeze" once you make photo
        
        previewLayer!.videoGravity = AVLayerVideoGravityResize
        // works perfectly on ios9, older devices etc.
        // on 6s+, you get a small jump between the video live preview and the make photo
        
        fixConnectionOrientation()
        
        view.layer.addSublayer(previewLayer!)
        captureSession!.startRunning()
        previewLayer!.frame = view.bounds
        }
    
/*Video Gravity.
These string constants define how the video is displayed within a layer’s bounds rectangle.
You use these constants when setting the videoGravity property of an AVPlayerLayer or AVCaptureVideoPreviewLayer instance.

AVLayerVideoGravityResize
Specifies that the video should be stretched to fill the layer’s bounds.

AVLayerVideoGravityResizeAspect
Specifies that the player should preserve the video’s aspect ratio and fit the video within the layer’s bounds.

AVLayerVideoGravityResizeAspectFill
Specifies that the player should preserve the video’s aspect ratio and fill the layer’s bounds.
*/

    func makePhotoOn(_ here:UIImageView)
        {
        // recall that this indeed makes a still image, which is used as
        // a new background image (indeed on the "stillImage" view)
        // and you can then continue to move the door around on that scene.
        
        if ( stillImageOutput == nil )
            {
            print("simulator, using test image.")
            here.image = UIImage(named:"ProductMouldings.jpg")
            return
            }

        guard let videoConnection = stillImageOutput!.connection(withMediaType: AVMediaTypeVideo)
        else
            {
            print("AVMediaTypeVideo didn't work?")
            return
            }
        
        videoConnection.videoOrientation = (previewLayer!.connection?.videoOrientation)!
            
        stillImageOutput?.captureStillImageAsynchronously(
            from: videoConnection, completionHandler:
                {
                (sampleBuffer, error) in
                guard sampleBuffer != nil else
                    {
                    print("sample buffer woe?")
                    return
                    }
                
                let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
                let dataProvider = CGDataProvider(data: imageData as! CFData)
                let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
                
                let ort = self.neededImageOrientation()
                let image = UIImage(cgImage:cgImageRef!, scale:1.0, orientation:ort)
                
                here.image = image
                })
        }
    
    
    func neededImageOrientation()->(UIImageOrientation)
        {
        var n : UIImageOrientation
        let currentDevice: UIDevice = UIDevice.current
        let orientation: UIDeviceOrientation = currentDevice.orientation
        switch orientation
            {
            case UIDeviceOrientation.portraitUpsideDown:
                n = .left
            case UIDeviceOrientation.landscapeRight:
                n = .down
            case UIDeviceOrientation.landscapeLeft:
                n = .up
            case UIDeviceOrientation.portrait:
                n = .right
            default:
                n = .right
            }
        return n
        }
    
    /*
    @IBAction func didPressTakeAnother(sender: AnyObject)
        { captureSession!.startRunning() }
    */
    
    }

【问题讨论】:

    标签: camera swift3 ios10 avcapturesession


    【解决方案1】:

    完整性检查 - 您确定 AVLayerVideoGravityResize 是您要使用的那个吗?这会将图像(不保留纵横比)拉伸到预览的框架。如果您打算保持纵横比,您要么想要AVLayerVideoGravityResizeAspect(如您所见,会有灰色条,但将保持纵横比)或AVLayerVideoGravityResizeAspectFill(可能是您想要的 - 部分预览将被剪切关闭,但将保持纵横比)。

    假设您的“此处”视图(传递给makePhotoOn: 的视图)与预览视图的大小/位置相同,您需要设置“此处”视图的contentMode 以匹配预览的行为。

    因此,如果您使用AVLayerVideoGravityResizeAspect 进行预览,那么:

    here.contentMode = .scaleAspectFit

    如果您使用AVLayerVideoGravityResizeAspectFill 进行预览,则:

    here.contentMode = .scaleAspectFill.

    视图的默认contentMode.scaleToFill(此处注明:https://developer.apple.com/reference/uikit/uiview/1622619-contentmode),因此您的“此处”imageView 可能会拉伸图像以匹配其大小,而不是保持纵横比。

    如果这没有帮助,您可以考虑在 github 上提供一个展示问题的准系统项目,以便我们中的 SO 修补者可以快速构建和修补它。

    【讨论】:

    • 谢谢 - 我发送了一个赏金来表示感谢,并将尽快调查此问题
    • 告诉我进展如何!
    • 对此进行扩展。当尝试捕获图像并将其放入预览层(例如拍照)时,应将图像设置为 charmToad 建议,确保图像视图已设置“剪辑到边界”
    猜你喜欢
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    • 2017-01-28
    • 2017-01-30
    相关资源
    最近更新 更多