AVCaptureVideoPreviewLayer(相机预览)freezes/stuck 移动到背景并返回后

AVCaptureVideoPreviewLayer (camera preview) freezes/stuck after moving to background and back

想不通这一点。 当应用程序处于活动状态时一切正常,而不是 有时 当我将应用程序移到后台(按主页按钮)而不是返回预览层 freezes/stuck。 我使用 viewWillAppear 和 viewDidAppear 进行设置。 这就是我设置一切的方式:

  var backCamera = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
  var global_device : AVCaptureDevice!
  var captureSession: AVCaptureSession?

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)

captureSession = AVCaptureSession()
        captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
        CorrectPosition = AVCaptureDevicePosition.Back
        for device in backCamera {
            if device.position == AVCaptureDevicePosition.Back {
                global_device = device as! AVCaptureDevice
                CorrectPosition = AVCaptureDevicePosition.Back
                break
            }
        }


        configureCamera()
        var error: NSError?
        var input = AVCaptureDeviceInput(device: global_device, error: &error)


        if error == nil && captureSession!.canAddInput(input) {
            captureSession!.addInput(input)

            stillImageOutput = AVCaptureStillImageOutput()
            stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
            if captureSession!.canAddOutput(stillImageOutput) {
                captureSession!.addOutput(stillImageOutput)

                previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
                var bounds:CGRect = camera_Preview.layer.bounds
                previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
                previewLayer?.bounds = bounds
                previewLayer?.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))
                camera_Preview.layer.addSublayer(previewLayer)
                self.view.bringSubviewToFront(camera_Preview)
                self.view.bringSubviewToFront(nan_view)

                captureSession!.startRunning()


            }
        }

ViewDidAppear :

  var previewLayer: AVCaptureVideoPreviewLayer?


override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        previewLayer!.frame = camera_Preview.bounds
    }

投资回报率,

我认为您的问题是您正在 viewWillAppear 中进行所有会话设置等。可以说 captureSession 和 previewLayer 都已分配并且工作正常。现在,您将应用程序置于后台并返回。

您将立即尝试创建一个新的 captureSession 和一个新的 previewLayer。怀疑是新旧纠缠

在 Apple AVCam 示例中,他们在 viewDidLoad 中进行设置。这样它只完成一次。

您应该将所有设置内容移至一个方法,然后从 viewDidLoad 调用该方法。

账单

我认为可能导致问题的原因有很多:

  1. 您应该将正在执行的所有配置包装在 -beginConfiguration-commitConfiguration 代码块中。每次您在会话中设置某些内容时,都需要时间来完成。在这些方法之间包装您的配置代码将保证一次性提交所有更改,减少整个会话创建时间
  2. 当您进入后台时暂停会话是个好办法。将您的 class 注册为 UIApplicationDidEnterBackgroundUIApplicationWillEnterForeground 的观察员以暂停并重新开始会话。
  3. 您在 -viewWillAppear 中创建会话,每次调用此方法时您都会创建一个会话,但是,如果您删除它,从您的代码中并不清楚。您应该分离和平衡会话的创建和销毁。提供一个 -setupSession 和一个 -tearDownSession 方法。确保只有在没有活动会话时才调用设置,并确保当您不再需要该会话时,您可以通过调用 teardownSession 来摆脱它。在 SWIFT 中,您不能使用 @lazy 变量并在 deinit()-viewWillDisappear.
  4. 中销毁会话
  5. 创建或使用 SYNC 队列可能非常有用,创建和销毁会话是一项密集型任务,您通常更喜欢将其放在后台队列中,此外还有助于同步涉及会话的所有方法。创建您自己的同步队列将保证例如设置会话调用和拆卸调用之间的同步,一个仅在另一个完成时调用。

我不明白这是一个巨大的重构,但我是这样的我很确定你将来会遇到更少的问题。

对于未来的读者:这是在您的应用中设置摄像头的正确过程。

首先,感谢上面那些花时间帮助我的人。他们都指引我走向正确的方向。尽管 Bill 对 viewDidLoad 理论有误,但他确实给出了 Apple Project 的解决方案。

这种设置相机的正确方法比我想象的要复杂一点,按照文档进行操作给了我很好的结果。所以对于 Objective-C 编码器:

Objective C cam project

Swift cam project

关于 Andrea 的回答,他确实提出了一些您在创建此类应用程序时应考虑的重要建议。查看它们 - 它们高度相关(他在 Apple 项目中所说的大部分内容也是如此)。

2017 年的快速更新,如果有人正在经历这样的事情,

做同样的事情,但改变你的

stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

替换为

stillImageOutput!.outputSettings = [((kCVPixelBufferPixelFormatTypeKey as NSString) as String):NSNumber(value:kCVPixelFormatType_32BGRA)]

它将解决问题。如果没有,那就回来写下:))

Swift 4 解

你必须在用户进入后台时删除相机输入,然后在他们return 时恢复它。看看这段代码:

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
         createCameraPreview() //Call the setup of the camera here so that if the user enters the view controller from another view controller, the camera is established. 
         notificationCenter() //Call the notification center function to determine when the user enters and leaves the background. 
    }



 func notificationCenter() {
            NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: .UIApplicationWillResignActive , object: nil)
                NotificationCenter.default.addObserver(self, selector: #selector(openedAgain), name: .UIApplicationDidBecomeActive, object: nil)
        }

 @objc func openedAgain() {
     createCameraPreview() // This is your function that contains the setup for your camera. 
    }

    @objc func willResignActive() {
        print("Entered background")
        let inputs = captureSession!.inputs
        for oldInput:AVCaptureInput in inputs {
            captureSession?.removeInput(oldInput)
        }
    }

Swift 4.2:

   override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
            setupCamera()
            //createCameraPreview() //Call the setup of the camera here so that if the user enters the view controller from another view controller, the camera is established.
            notificationCenter() //Call the notification center function to determine when the user enters and leaves the background.
    }



    func notificationCenter() {
        NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willResignActiveNotification , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(openedAgain), name: UIApplication.didBecomeActiveNotification, object: nil)
    }

    @objc func openedAgain() {
        setupCamera() //This is your function that contains the setup for your camera.
    }

    @objc func willResignActive() {
        print("Entered background")
        let inputs = captureSession.inputs
        for oldInput:AVCaptureInput in inputs {
            captureSession.removeInput(oldInput)
        }
    }

我也遇到了这个问题,我通过将其添加到我的 viewWillAppear 和 viewWillDissapear 来修复我的问题。希望这有帮助

var session = AVCaptureSession()

override func viewWillAppear(_ animated: Bool) {
        session.startRunning()

    }

override func viewWillDisappear(_ animated: Bool) {
        session.stopRunning()
    }