AVCaptureVideoPreviewLayer (предварительный просмотр камеры) зависает/застревает после перемещения на задний план и обратно

Не могу изобразить это. Все работает нормально, когда приложение активно, и иногда, когда я перемещаю приложение в фоновый режим (нажатие кнопки "домой" ), а затем возвращается, предварительный просмотр замораживается/застревает. Im использует 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
    }

Ответы

Ответ 1

Для будущих читателей: это правильный процесс настройки камеры внутри вашего приложения.

Прежде всего, спасибо, что люди, вышедшие из этого, не спешили и попробовали помочь мне. Они оба направляют меня в правильном направлении. Хотя Билл ошибался в теории viewDidLoad, он дал решение Apple Project.

Эта настройка камеры - правильный путь - немного сложнее, чем я думал, следуя документации дал мне отличные результаты. Итак, для кодеров Objective-C:

Проект Objective C cam

Проект Swift cam

Об Андреа ответьте, он сказал несколько отличных указателей, которые вы должны учитывать при создании такого приложения. Проверьте их - они очень актуальны (большинство вещей, которые он сказал в проекте Apple также).

Ответ 2

Руа,

Я думаю, ваша проблема в том, что вы выполняете всю настройку сеанса и т.д. в viewWillAppear. Допустим, что captureSession и previewLayer были как alloc'd, так и корректно работают. Теперь вы помещаете приложение в фоновый режим и возвращаете.

Вы сразу же попытаетесь создать новую captureSession и новый файл предварительного просмотра. Я подозреваю, что старые и новые запутались.

В примере Apple AVCam они выполняют настройку в viewDidLoad. Таким образом, это делается только один раз.

Вы должны перенести все свои материалы настройки в метод, а затем вызвать метод из viewDidLoad.

банкнота

Ответ 3

Просто быстрое обновление. В 2017 году, если кто-то страдает от чрезмерного мышления такой вещи,

Сделайте то же самое, но измените свой

stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

замените его на

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

он решит проблему. Если нет, вернитесь сюда и запишите:))

Ответ 4

Я думаю, что есть разные вещи, которые могут вызвать проблему:

  • Вы должны обернуть всю конфигурацию, которую вы выполняете в блоке кода -beginConfiguration и -commitConfiguration. Каждый раз, когда вы настраиваете что-то в сеансе, для этого потребуется время. Объединение кода конфигурации между этими методами гарантирует, что все изменения будут совершены за один снимок, что уменьшит общее время создания сеанса.
  • Приостановка сеанса - это хорошо, когда вы идете на задний план. Зарегистрируйте свой класс в качестве наблюдателя в UIApplicationDidEnterBackground и UIApplicationWillEnterForeground для приостановки и начала сеанса.
  • Вы создаете сеанс в -viewWillAppear каждый раз, когда этот метод вызывается, вы создаете сеанс, но на самом деле не совсем ясно из вашего кода, если вы избавитесь от него. Вы должны разделить и сбалансировать создание сеанса и уничтожить его. Предоставьте методы -setupSession и -tearDownSession. Убедитесь, что настройка вызывается только в том случае, если нет активного сеанса, и убедитесь, что, когда вам больше не нужен сеанс, вы избавитесь от него, вызвав teardownSession. В SWIFT вы используете переменную @lazy и уничтожаете сеанс в deinit() или -viewWillDisappear.
  • Было бы очень полезно создавать или использовать очередь SYNC, создавая и разрушая сеанс, является интенсивной задачей, и вы обычно предпочитаете помещать ее в фоновый режим, а также помогает синхронизировать все методы, связанные с сеансом. Создание собственной очереди синхронизации гарантирует, например, синхронизацию между сеансом настройки сеанса и разрывом вызова, один вызывается только тогда, когда другой заканчивается.

Я понял, что это огромный рефактор, но я так уверен, что в будущем у вас будет меньше проблем.

Ответ 5

Swift 4 Solution

Вы должны удалить вход камеры, когда пользователь вводит фон, а затем восстановить его, когда он вернется. Посмотрите на этот код:

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

Ответ 6

Я тоже столкнулся с этой проблемой, я исправил мою, добавив ее в мои viewWillAppear и viewWillDissapear. Надеюсь это поможет

var session = AVCaptureSession()

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

    }

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