AvCaptureSession代码提取错误

AvCaptureSession code extraction error

我试图从我的视图控制器中提取一些代码来清理它并尽可能保持代码干燥。它在以下情况下工作正常:

class InitialRegistrationViewController: UIViewController, UINavigationControllerDelegate,  UIImagePickerControllerDelegate, NVActivityIndicatorViewable {

    var session: AVCaptureSession?
    var stillImageOutput: AVCaptureStillImageOutput?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if Platform.isPhone {
            session = AVCaptureSession()
            session!.sessionPreset = AVCaptureSessionPresetPhoto

            var frontCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
            let availableCameraDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)
            for device in availableCameraDevices as! [AVCaptureDevice] {
                if device.position == .front {
                    frontCamera = device
                }
            }

            var error: NSError?
            var input: AVCaptureDeviceInput!
            do {
                input = try AVCaptureDeviceInput(device: frontCamera)
            } catch let error1 as NSError {
                error = error1
                input = nil
                print(error!.localizedDescription)
            }

            if error == nil && session!.canAddInput(input) {
                session!.addInput(input)
                stillImageOutput = AVCaptureStillImageOutput()
                stillImageOutput?.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

                if session!.canAddOutput(stillImageOutput) {
                    session!.addOutput(stillImageOutput)
                    session!.startRunning()
                }
            }
        }
    }


    func capturePhoto() {
        if let videoConnection = stillImageOutput!.connection(withMediaType: AVMediaTypeVideo) {
            stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (sampleBuffer, error) -> Void in
                if sampleBuffer != nil {
                    let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
                    let dataProvider = CGDataProvider(data: imageData as! CFData)
                    let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
                    let image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.right)
                    self.profileImage.image = image
                }
            })
        }
    }
}

但是当我提取到如下助手时:

导入 UIKit 导入 AVFoundation

class ProfilePhoto {

    var session: AVCaptureSession?
    var stillImageOutput: AVCaptureStillImageOutput?

    func startSession() {

        if Platform.isPhone {
            session = AVCaptureSession()
            session!.sessionPreset = AVCaptureSessionPresetPhoto

            var frontCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
            let availableCameraDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)
            for device in availableCameraDevices as! [AVCaptureDevice] {
                if device.position == .front {
                    frontCamera = device
                }
            }

            var error: NSError?
            var input: AVCaptureDeviceInput!
            do {
                input = try AVCaptureDeviceInput(device: frontCamera)
            } catch let error1 as NSError {
                error = error1
                input = nil
                print(error!.localizedDescription)
            }

            if error == nil && session!.canAddInput(input) {
                session!.addInput(input)
                stillImageOutput = AVCaptureStillImageOutput()
                stillImageOutput?.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

                if session!.canAddOutput(stillImageOutput) {
                    session!.addOutput(stillImageOutput)
                    session!.startRunning()
                }
            }
        }
    }

    func capture() -> UIImage {
        var image: UIImage!

        if let videoConnection = stillImageOutput!.connection(withMediaType: AVMediaTypeVideo) {
            stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (sampleBuffer, error) -> Void in
                if sampleBuffer != nil {
                    let cgImageRef = self.setBufferData(sampleBuffer: sampleBuffer!)
                    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: UIImageOrientation.right)
                }
            })
        }
        return image
    }

    func setBufferData(sampleBuffer: CMSampleBuffer ) -> CGImage {
        let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
        let dataProvider = CGDataProvider(data: imageData as! CFData)
        let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
        return cgImageRef!
    }
}

我调用 InitialRegistrationViewController 的地方:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    profilePhoto.startSession()
}


func capturePhoto() {
    profileImage.image = profilePhoto.capture()
}

在 profilePhoto.capture() 中返回图像时我得到 fatal error: unexpectedly found nil while unwrapping an Optional value

我不明白会话是如何工作的,因为我是 ios 的新手,但我认为这是因为当我尝试捕获图像时会话正在结束(?)?任何见解都会很棒。谢谢

更新:我赞成给出的答案,因为它足够接近,下面是对我有用的答案。

    func capture(completion: @escaping (UIImage?) -> Void) {

    if let videoConnection = stillImageOutput!.connection(withMediaType: AVMediaTypeVideo) {
        stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (sampleBuffer, error) -> Void in
            if sampleBuffer != nil {
                let cgImageRef = self.setBufferData(sampleBuffer: sampleBuffer!)
                let image: UIImage! = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: UIImageOrientation.right)
                completion(image)
            } else {
                completion(nil)
            }
        })
    } else {
        completion(nil)
    }
}

您的 capture() 方法进行 异步 调用以获取 UIImage,因此当它 returns [立即] 值 returned 是总是 nil.

@dan 建议的文章显示了一个回调模式,可用于 return 调用者的图像,请确保您在继续之前了解此机制。

    func capture(result: (image: UIImage?) -> Void) -> UIImage 
    {
        if let videoConnection = stillImageOutput!.connection(withMediaType: AVMediaTypeVideo) 
        {
            stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler:
            { (sampleBuffer, error) -> Void in

             if sampleBuffer != nil 
             {
                let cgImageRef = self.setBufferData(sampleBuffer: sampleBuffer!)
                var image: UIImage! = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: UIImageOrientation.right)
                result(image: image)
             }
             else
                result(image: nil)   

            })
        }
        else
            result(image: nil)
    }

要调用它,您可以使用

capture(){(image: UIImage?) -> Void in
    //use the image that was just retrieved
}

您现在已将 capture() 方法设为异步,并通过回调报告其 return 值。