Сбой в AVCaptureSession при добавлении AVCaptureDeviceInput

У меня странный сбой, отображаемый на Crashlytics при настройке сеанса камеры. Трассировка стека показывает, что сбой произошел в методе addInput.

func setupCamSession(){
    self.captureSession = AVCaptureSession()
    self.cameraView.setSession(self.captureSession)
    self.sessionQueue = dispatch_queue_create("com.myapp.camera_queue", DISPATCH_QUEUE_SERIAL)
    self.setupResult = .Success
    switch AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo){
    case .Authorized:
        break //already we set it to success
    case .NotDetermined:
        // The user has not yet been presented with the option to grant video access.
        // We suspend the session queue to delay session setup until the access request has completed 
        dispatch_suspend(self.sessionQueue)
        AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (granted) -> Void in
            if ( !granted ) {
                self.setupResult = .CameraNotAuthorized
            }
            dispatch_resume(self.sessionQueue)
        })
    default:
        self.setupResult = .CameraNotAuthorized
    }

    dispatch_async(self.sessionQueue){
        if self.setupResult != .Success{
            return
        }
        //link input to captureSession
        guard let videoDevice = self.deviceWithMediaType(AVMediaTypeVideo, position: AVCaptureDevicePosition.Back) else{
            AppLog("Video Device Unavailable")
            self.setupResult = .SessionConfigurationFailed
            return
        }
        var videoDeviceInput: AVCaptureDeviceInput!
        do {
            videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice)
        }catch {
            AppLog("Could not create video device input")
        }

        /////////////////////////////////////////////////////
        self.captureSession.beginConfiguration()

        if self.captureSession.canAddInput(videoDeviceInput){
            self.captureSession.addInput(videoDeviceInput)
            self.videoDeviceInput = videoDeviceInput
            self.videoDevice = videoDevice
            dispatch_async(dispatch_get_main_queue()){
                //update the cameraView layer on the main thread
                let previewLayer : AVCaptureVideoPreviewLayer = self.cameraView.layer as! AVCaptureVideoPreviewLayer
                previewLayer.connection.videoOrientation = AVCaptureVideoOrientation(ui:UIApplication.sharedApplication().statusBarOrientation)
            }
        }else{
            AppLog("Could not add video device input to the session")
            self.setupResult = .SessionConfigurationFailed
        }

        //link output to captureSession
        let stillImageOutput = AVCaptureStillImageOutput()
        if self.captureSession.canAddOutput(stillImageOutput){
            self.captureSession.addOutput(stillImageOutput)
            self.stillImageOutput = stillImageOutput
            stillImageOutput.outputSettings = [AVVideoCodecKey : AVVideoCodecJPEG]
        }else{
            AppLog("Could not add still image output to the session")
            self.setupResult = .SessionConfigurationFailed
        }

        self.captureSession.commitConfiguration()
        /////////////////////////////////////////////////////
    }
}

func runSession(){
    dispatch_async(self.sessionQueue){
        switch self.setupResult!{
        case .Success:
            self.videoDeviceInput!.device.addObserver(self, forKeyPath: "adjustingFocus", options: NSKeyValueObservingOptions.New, context: nil)
            self.captureSession.addObserver(self, forKeyPath: "running", options: [.New], context: &SessionRunningContext)
            self.captureSession.startRunning()
            self.captureSessionRunning = self.captureSession.running
            if !self.captureSessionRunning {
                self.captureSession.removeObserver(self, forKeyPath: "running", context: &SessionRunningContext)
                self.videoDeviceInput?.device?.removeObserver(self, forKeyPath: "adjustingFocus", context: nil)
            }
       default:
       //Handle errors.
       }
  }
func stopCaptureSession(){
    dispatch_async(self.sessionQueue){
        if self.setupResult == .Success{
            if self.captureSessionRunning{
                self.captureSession.stopRunning()
                self.videoDeviceInput?.device?.removeObserver(self, forKeyPath: "adjustingFocus", context: nil)
                self.captureSession.removeObserver(self, forKeyPath: "running", context: &SessionRunningContext)
            }
            self.captureSessionRunning = false
        }
    }
}

setupCamSession вызывается в viewDidLoad, runSession в viewWillAppear, и у меня также есть метод stopSession в viewWillDisappear. Все, что связано с сеансом камеры, отправляется в фоновую последовательную очередь.

Сбой не происходит в 100% случаев, и я не могу воспроизвести сбой на используемом устройстве. Спасибо Это скриншот трассировки стека


person ELKA    schedule 03.06.2016    source источник
comment
переместите его в viewWillAppear или viewDidAppear   -  person Leo Dabus    schedule 03.06.2016
comment
Почему? Я предпочитаю держать его в viewDidLoad(). Фактически, на главном экране есть вид с камеры, и мы много раз нажимаем и выталкиваем контроллеры вида на главном экране. Таким образом, ViewWillAppear будет вызываться много раз и каждый раз будет снова устанавливать сеанс камеры. В настоящее время работает только сеанс камеры.   -  person ELKA    schedule 03.06.2016
comment
Меня вдохновил developer.apple.com/library/ios/samplecode/AVCam/Listings/. Они настраивают ввод в viewDidLoad()   -  person ELKA    schedule 03.06.2016
comment
Есть ли какая-либо дополнительная информация в отчете о сбое? Как имя селектора, найденное в регистрах аргументов?   -  person jtbandes    schedule 03.06.2016
comment
Нет. Это все, что у меня есть в отчете.   -  person ELKA    schedule 03.06.2016


Ответы (2)


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

person Chris Wagner    schedule 24.08.2016
comment
Я отредактировал свой вопрос и добавил метод stopCaptureSession(). Этот метод удалял наблюдателя для настройки фокуса. В журналах говорится, что наблюдатели были удалены, после чего был вызван deinit. - person ELKA; 09.09.2016
comment
Возможно ли так или иначе, что videoDeviceInput.device станет равным нулю? Если это произойдет, self.videoDeviceInput?.device?.removeObserver(self, forKeyPath: "adjustingFocus", context: nil) вызываться не будет. - person ELKA; 09.09.2016
comment
Большое спасибо. Это была именно моя проблема. - person Seifolahi; 25.06.2019

Была такая же проблема. Проблема была решена после добавления описания использования для Privacy - Camera Usage Description в файл Info.plist. Этот ответ содержит советы о том, как настроить описание:

Запросить разрешение для камеры и библиотеки в iOS 10 — Info.plist

person robbdimitrov    schedule 14.05.2018