web-dev-qa-db-ja.com

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

将来の読者のために:これは、アプリ内にカメラを設定するための正しいプロセスです。

まず第一に、時間をかけて私を助けてくれた上記の人々に感謝します。彼らは両方とも私を正しい方向に導きます。ビルはviewDidLoad理論について誤解していましたが、彼は解決策Appleプロジェクトを提供しました。

このカメラのセットアップ(正しい方法)は、私が思っていたよりも少し複雑です。ドキュメントに従うと、優れた結果が得られました。したがって、Objective-Cコーダーの場合:

Objective C camプロジェクト

Swift camプロジェクト

アンドレアの答えについて、彼はあなたがこの種のアプリを作成するときに考慮すべきいくつかの素晴らしい指針を述べました。それらをチェックしてください-それらは非常に関連性があります(彼がAppleプロジェクト内でも言ったことのほとんど)。

4
Roi Mulia

ロイ、

あなたの問題は、viewWillAppearですべてのセッションセットアップなどを行っていることだと思います。 CaptureSessionとpreviewLayerの両方が割り当てられ、正しく機能しているとしましょう。ここで、アプリをバックグラウンドに配置して戻します。

すぐに、新しいcaptureSessionと新しいpreviewLayerを作成しようとします。古いものと新しいものが絡み合っているのではないかと思います。

Apple AVCamの例では、viewDidLoadでセットアップを実行します。これにより、セットアップは1回だけ実行されます。

すべてのセットアップをメソッドに移動してから、viewDidLoadからメソッドを呼び出す必要があります。

ビル

4
Bill Johnson

ちょっとした更新2017年に誰かがそのようなことを考えすぎて苦しんでいるなら、

同じことをしますが、

stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

に置き換えます

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

それは問題を解決します。そうでない場合は、ここに戻って書き留めてください:))

3
ozan

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

問題を引き起こす可能性のあるさまざまなことがあると思います。

  1. 実行しているすべての構成を_-beginConfiguration_および_-commitConfiguration_ブロックのコードでラップする必要があります。セッションで何かをセットアップするたびに、それを行うのに時間がかかります。これらのメソッド間で構成コードをラップすると、すべての変更が1回のショットでコミットされ、セッション全体の作成時間が短縮されます。
  2. バックグラウンドに移動するときは、セッションを一時停止することをお勧めします。クラスをオブザーバーとしてUIApplicationDidEnterBackgroundUIApplicationWillEnterForegroundに登録して、セッションを一時停止して再開します。
  3. このメソッドが呼び出されるたびに_-viewWillAppear_でセッションを作成しますが、セッションを削除すると、コードからは明確になりません。セッションの作成と破棄を分離してバランスを取る必要があります。 _-setupSession_メソッドと_-tearDownSession_メソッドを提供します。アクティブなセッションがない場合にのみセットアップが呼び出されることを確認し、セッションが不要になったときにteardownSessionを呼び出してセッションを削除するようにします。 Swiftでは、@ lazy変数を使用し、deinit()または_-viewWillDisappear_でセッションを破棄します。
  4. SYNCキューを作成または使用すると、セッションの作成と破棄は集中的なタスクであり、通常はバックグラウンドキューに配置することを好み、さらにセッションに関連するすべてのメソッドを同期するのに役立ちます。独自の同期キューを作成すると、たとえば、セットアップセッション呼び出しとティアダウン呼び出しの間の同期が保証されます。一方は、もう一方が終了したときにのみ呼び出されます。

それが大きなリファクタリングであることは理解できませんが、このようにして、将来的に問題が少なくなると確信しています。

1
Andrea

私もこの問題に直面しました。これをviewWillAppearとviewWillDissapearに追加して修正しました。お役に立てれば

var session = AVCaptureSession()

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

    }

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