3

I have a weird crash showing on Crashlytics when setting up a camera session. The stacktrace shows that the crash occurred at the method 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
        }
    }
}

The setupCamSession is called in viewDidLoad, the runSession in viewWillAppear and I have also a stopSession method in viewWillDisappear. Everything related to the camera session is dispatched on a background serial queue.

The crash doesn't happen 100% of the time and I am unable to reproduce the crash on the device I use. Thanks This is a screenshot of the stacktrace

ELKA
  • 735
  • 6
  • 17
  • move it to viewWillAppear or viewDidAppear – Leo Dabus Jun 03 '16 at 05:16
  • Why? I prefer to keep it in viewDidLoad(). In fact, the home screen has the camera view and we are pushing and popping view controllers many times on the home screen. So ViewWillAppear will be called many times and each time it will setup the camera session again. Currently it is only running the camera session. – ELKA Jun 03 '16 at 05:23
  • I was inspired by https://developer.apple.com/library/ios/samplecode/AVCam/Listings/AVCam_AAPLCameraViewController_m.html#//apple_ref/doc/uid/DTS40010112-AVCam_AAPLCameraViewController_m-DontLinkElementID_6. They setup the input in viewDidLoad() – ELKA Jun 03 '16 at 05:24
  • Is there any more info in the crash report? Like a "selector name found in argument registers"? – jtbandes Jun 03 '16 at 05:26
  • No. That's all I have in the report. – ELKA Jun 03 '16 at 05:33

2 Answers2

4

Make sure you are removing observers on deinit. I saw this occurring when coming back to the camera capture screen and I didn't remove the observer for adjustingFocus. Once I removed that in deinit all was well.

Chris Wagner
  • 20,773
  • 8
  • 74
  • 95
  • I edited my question and added the stopCaptureSession() method. This method was removing the observer for adjustingFocus. The logs are saying that the observers have been removed then the deinit has been called. – ELKA Sep 09 '16 at 06:45
  • Is it possible in a way or another that videoDeviceInput.device becomes nil? If this happens `self.videoDeviceInput?.device?.removeObserver(self, forKeyPath: "adjustingFocus", context: nil)` will not be called. – ELKA Sep 09 '16 at 06:52
  • Thanks a lot. That was my problem exactly. – Seifolahi Jun 25 '19 at 13:36
2

Had the same problem. It was resolved after adding an usage description for the Privacy - Camera Usage Description in the Info.plist file. This answer contains tips on how to set up the description:

Request Permission for Camera and Library in iOS 10 - Info.plist

robbdimitrov
  • 149
  • 3
  • 12