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

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

5 ответов

Решение

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

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

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

Цель C Cam проект

Swift Cam проект

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

Roi,

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

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

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

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

законопроект

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

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

stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

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

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

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

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

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

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

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

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

var session = AVCaptureSession()

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

    }

override func viewWillDisappear(_ animated: Bool) {
        session.stopRunning()
    }
Другие вопросы по тегам