web-dev-qa-db-fra.com

AVCaptureVideoPreviewLayer (aperçu de la caméra) se fige/reste bloqué après avoir été déplacé vers l'arrière-plan

Tout fonctionne bien lorsque l’application est active et que parfois lorsque je déplace l’application en arrière-plan (en appuyant sur le bouton principal) et que pour revenir en arrière, la couche de prévisualisation se bloque/coincé. J'utilise viewWillAppear et viewDidAppear pour la configuration. Voici comment j'ai tout configuré:

  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
    }
17
Roi Mulia

Pour les futurs lecteurs: il s'agit du processus approprié pour configurer la caméra dans votre application.

Tout d'abord, merci aux personnes ci-dessus qui ont pris leur temps et ont essayé de m'aider. Ils me dirigent tous les deux dans la bonne direction. Bien que Bill se soit trompé sur la théorie viewDidLoad, il a donné la solution Apple Project.

Cette configuration de la caméra - la manière correcte - est un peu plus compliquée que je ne le pensais, suivre la documentation m'a donné d'excellents résultats. Donc pour les codeurs Objective-C:

Projet Objective C cam

Projet Swift cam

À propos de la réponse d’Andrea, il a indiqué que vous devriez prendre en considération d’excellents conseils lorsque vous créez ce type d’application. Vérifiez-les - ils sont très pertinents (la plupart des choses qu'il a dites dans le projet Apple également).

3
Roi Mulia

Juste une mise à jour rapide en 2017 si quelqu'un souffre trop de penser à ça,

Faites la même chose mais changez votre

stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

le remplacer par

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

cela résoudra le problème. Si non, alors reviens ici et écris :))

3
ozan

Roi,

Je pense que votre problème est que vous effectuez toute la configuration de la session et telle dans le viewWillAppear. Disons que la captureSession et le previewLayer étaient tous deux alloués et fonctionnaient correctement. Maintenant, vous mettez l'application en arrière-plan et ramenez-la.

Vous allez immédiatement essayer de créer une nouvelle captureSession et un nouveau previewLayer. Je soupçonne que les anciens et les nouveaux s'emmêlent.

Dans l'exemple Apple AVCam, ils effectuent la configuration dans viewDidLoad. De cette façon, ce n'est fait qu'une fois. 

Vous devez déplacer tous vos éléments de configuration vers une méthode, puis appeler la méthode à partir de viewDidLoad. 

facture

2
Bill Johnson

Je pense qu'il y a différentes choses qui pourraient causer le problème:

  1. Vous devez envelopper toute la configuration que vous effectuez dans un bloc de code -beginConfiguration et -commitConfiguration. Chaque fois que vous configurez quelque chose dans la session, cela prend du temps. En plaçant votre code de configuration entre ces méthodes, vous garantissez que toutes les modifications sont validées en une seule fois, ce qui réduit le temps de création de la session
  2. Mettre en pause la session est une bonne chose quand vous allez au fond. Inscrivez votre classe en tant qu'observateur sur UIApplicationDidEnterBackground et UIApplicationWillEnterForeground pour mettre en pause et redémarrer la session.
  3. Vous créez la session dans -viewWillAppear chaque fois que cette méthode est appelée, vous créez une session, mais votre code ne le clarifie pas vraiment si vous vous en débarrassez. Vous devez séparer et équilibrer la création et la destruction de la session. Fournissez les méthodes -setupSession et -tearDownSession. Assurez-vous que la configuration est appelée uniquement s'il n'y a pas de session active et assurez-vous que lorsque vous n'en avez plus besoin, vous vous en débarrassez en appelant le teardownSession. Dans Swift, vous utilisez une variable @lazy et détruisez la session dans deinit() ou -viewWillDisappear.
  4. Il peut être très utile de créer ou d’utiliser une file d’attente SYNC. La création et la destruction d’une session sont des tâches ardues. Vous préférez généralement les placer dans une file d’arrière-plan, ce qui facilite également la synchronisation de toutes les méthodes impliquant la session. La création de votre propre file d'attente de synchronisation garantira, par exemple, la synchronisation entre un appel de session de configuration et un appel de démontage, l'un des appels n'étant appelé que lorsque l'autre finissant.

Je comprends que c'est un refactor énorme, mais je suis comme ça, je suis à peu près sûr que vous aurez moins de problèmes à l'avenir.

1
Andrea

Solution Swift 4

Vous devez supprimer l'entrée de la caméra lorsque l'utilisateur entre en arrière-plan, puis la restaurer à son retour. Jetez un oeil à ce code:

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:

remplacez func viewWillAppear (_ animé: Bool) { super.viewWillAppear (animé) setupCamera () // createCameraPreview () // Appelez la configuration de la caméra ici pour qu'elle si l'utilisateur entre le contrôleur de vue à partir d'un autre contrôleur de vue, la caméra est établie. notificationCenter () // Appelez la fonction de centre de notification pour déterminer quand l'utilisateur entre et quitte l'arrière-plan. }

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)
    }
}
1
Carter Cobb