Ios AVCaptureVideoPreviewLayer(相机预览)在移动到背景和返回后冻结/卡住

Ios AVCaptureVideoPreviewLayer(相机预览)在移动到背景和返回后冻结/卡住,ios,camera,preview,avcapturesession,avcapturedevice,Ios,Camera,Preview,Avcapturesession,Avcapturedevice,我想不出这个。 当应用程序处于活动状态时,一切正常,有时当我将应用程序移到后台(按home按钮)并返回时,previewlayer冻结/卡住。 Im使用ViewWillDisplay和ViewDidDisplay进行设置。 这就是我设置一切的方式: var backCamera = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) var global_device : AVCaptureDevice! var captur

我想不出这个。 当应用程序处于活动状态时,一切正常,有时当我将应用程序移到后台(按home按钮)并返回时,previewlayer冻结/卡住。 Im使用ViewWillDisplay和ViewDidDisplay进行设置。 这就是我设置一切的方式:

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


            }
        }
视图显示:

  var previewLayer: AVCaptureVideoPreviewLayer?


override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        previewLayer!.frame = camera_Preview.bounds
    }
投资回报率

我认为您的问题在于您正在进行所有会话设置,并且会在视图中显示这些设置。假设captureSession和previewLayer都已分配且工作正常。现在,您将应用程序放在后台并带回

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

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

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


比尔

我认为有不同的因素可能导致这个问题:

  • 您应该将正在执行的所有配置包装在
    -beginConfiguration
    -commitConfiguration
    代码块中。每次在会话中设置某些内容都需要时间。在这些方法之间包装配置代码将确保所有更改都在一次提交中提交,从而缩短整个会话创建时间
  • 当您转到后台时,暂停会话是一个很好的选择。将您的类注册为
    uiapplicationidenterbackground
    的观察者,然后
    UIApplicationWillEnterForeground
    暂停并重新启动会话
  • 每次调用此方法时,您都会在
    -view中创建会话,但如果您删除了会话,则代码中的会话并不清晰。您应该分离并平衡会话创建和销毁。提供
    -setupSession
    -tearDownSession
    方法。确保仅当没有活动会话时才调用安装程序,并确保当您不再需要会话时,可以通过调用
    teardownSession
    来摆脱会话。在SWIFT中,使用@lazy变量并在
    deinit()
    -viewwilldiscover
    中销毁会话
  • 创建或使用同步队列可能非常有用创建和销毁会话是一项密集型任务,您通常更喜欢将其放在后台队列中,而且有助于同步涉及会话的所有方法。例如,创建自己的同步队列将保证安装会话调用和拆除调用之间的同步,只有在另一个完成时才会调用其中一个

  • 我不理解这是一个巨大的重构,但我相信,通过这种方式,我相信你将来会遇到更少的问题。

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

    首先,感谢上面那些花时间帮助我的人。他们都指引我进入正确的方向。尽管比尔对
    viewDidLoad
    理论有误解,但他确实给出了苹果项目的解决方案

    这个设置相机-正确的方法-比我想象的要复杂一点,按照文档给了我极好的效果。因此,对于Objective-C编码器:


    关于安德列的回答,他说了一些你在创建这种应用程序时应该考虑的好的指针。看看它们——它们是高度相关的(他在苹果项目中所说的大部分内容也是如此)。

    如果有人过度思考这种事情,请在2017年快速更新

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

    stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
    
    换成

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

    它将解决这个问题。如果没有,请返回此处并写下:)

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

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

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

    比尔,谢谢你的回复。应用程序的进出是否会重新触发WillAspect等方法?实际上我刚刚检查了它,在ViewWillAspect中设置了一个断点,当我重新打开appRight时,似乎不会再次触发。最小化和返回时,ViewWillDisplay似乎不会被激发。但是,如果您继续使用另一个视图控制器,它将是。这可能是问题的一部分吗?你的意思是如果我从原始视图移动后出去?如果你在导航控制器中,按下第二个屏幕,然后按下后退按钮,你将导致我描述的问题。您的捕获会话和预览层将被重新创建。您可能希望将捕获会话创建移动到viewDidLoad。嘿!我使用基于苹果的文档来修复我的代码。你说的大部分我都加了,有必要做拆卸部分吗?因为我没有这样做,而且现在看起来很有效,谢谢!“目标C cam项目”的链接已断开。答案现在对任何人都没有用处。你能更新链接吗?或者在答案中显示解决方案?链接已更新。未来展望:如果链接再次中断,该项目将被称为“AVCam iOS”——谷歌会立即找到它,或者在developer.apple.com上查看它。如果苹果有像样的文档,事情可能会变得非常简单。像往常一样抛出示例代码或编写糟糕的文档对开发人员没有任何好处。想象一个没有stackoverflow的世界,没有一行代码可以创建。你知道原因吗?你能提供简单而简短的解决方案吗?