【问题标题】:AVCaptureVideoPreviewLayer (camera preview) freezes/stuck after moving to background and backAVCaptureVideoPreviewLayer(相机预览)在移动到背景和返回后冻结/卡住
【发布时间】:2015-05-30 00:12:18
【问题描述】:

想不出来这个。 当应用程序处于活动状态时一切正常,而 有时 当我将应用程序移动到后台(按主页按钮)而不是返回时,预览层会冻结/卡住。 我使用 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
    }

【问题讨论】:

  • 你能提供简单而简短的解决方案吗?

标签: ios camera preview avcapturesession avcapturedevice


【解决方案1】:

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

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

这个设置相机 - 正确的方法 - 比我想象的要复杂一些,按照文档给了我很好的结果。所以对于 Objective-C 的程序员来说:

Objective C cam project

Swift cam project

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

【讨论】:

  • “Objective C cam 项目”的链接已失效。答案现在对任何人都没有用。您能否更新链接或以某种方式在答案中显示解决方案?
  • 链接已更新。未来:如果链接再次断开,该项目称为“AVCam-iOS” - 谷歌会立即找到它或在 developer.apple.com 上查看它。
  • 如果 Apple 有不错的文档,事情就会变得如此简单。像往常一样抛出示例代码或编写不佳的文档对开发人员没有任何好处。想象一个没有 stackoverflow 的世界,也无法创建任何一行代码。
【解决方案2】:

罗伊,

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

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

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

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

账单

【讨论】:

  • 比尔,感谢您的回复。进出应用会重新触发 WillAppear 等方法吗?
  • 我其实只是检查了一下,在viewwillAppear中放了一个断点,当我重新打开应用时似乎没有再次触发
  • 没错。 ViewWillAppear 似乎不会在最小化和返回时被触发。但是,如果您对另一个视图控制器进行排序,它将是。这可能是麻烦的一部分吗?
  • 你的意思是如果我离开原来的视图后我就出去了?
  • 如果您在导航控制器中并按下第二个屏幕,然后点击后退按钮,您将导致我所描述的问题。您的捕获会话和预览层将被重新创建。您可能希望将捕获会话创建移至 viewDidLoad。
【解决方案3】:

2017 年的快速更新,如果有人想太多这样的事情,

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

stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

替换为

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

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

【讨论】:

  • 你知道这是什么原因吗?
【解决方案4】:

Swift 4 解决方案

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

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)
        }
    }

【讨论】:

    【解决方案5】:

    我认为有不同的事情可能会导致问题:

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

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

    【讨论】:

    • 嘿!我使用基于 Apple 的文档来修复我的代码。我确实添加了你说的大部分内容,是否有必要做tearDown部分?因为我没有,现在它似乎工作了,谢谢!
    【解决方案6】:

    我也遇到了这个问题,我通过将它添加到我的 viewWillAppear 和 viewWillDissapear 来解决我的问题。希望这会有所帮助

    var session = AVCaptureSession()
    
    override func viewWillAppear(_ animated: Bool) {
            session.startRunning()
    
        }
    
    override func viewWillDisappear(_ animated: Bool) {
            session.stopRunning()
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-13
      • 2012-10-14
      • 2013-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多