Capture picture from iOS camera

2019-01-20 18:34发布

问题:

I need to capture an image through the camera. But it gives the following error: 'NSInvalidArgumentException', reason: 'Cannot instantiate a AVCaptureDevice directly.'

Follows the code:

    var session: AVCaptureSession!
    var device: AVCaptureDevice!
    var captureVideoPreviewLayer: AVCaptureVideoPreviewLayer!
    var image: UIImage!
    var input: AVCaptureDeviceInput!
    var stillImageOutput: AVCaptureStillImageOutput!
    var viewControllerScheduling: RentalSchedulingViewController!

    override func viewDidLoad() {
        super.viewDidLoad()

        session = AVCaptureSession()
        session.sessionPreset = AVCaptureSessionPresetPhoto;
        captureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
        captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
        captureVideoPreviewLayer.frame = self.view.frame
        self.view.layer.addSublayer(captureVideoPreviewLayer)
        var error = NSErrorPointer()
        captureVideoPreviewLayer.session.beginConfiguration()
        input = AVCaptureDeviceInput(device: self.getBackCamera(), error: error)
        session.addInput(input)
        captureVideoPreviewLayer.session.commitConfiguration()
        stillImageOutput = AVCaptureStillImageOutput()
        var outputSettings = NSDictionary(objectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey)
        stillImageOutput.outputSettings = outputSettings
        session.addOutput(stillImageOutput)
        session.startRunning()
    }

Get back camera:

    func getBackCamera() -> AVCaptureDevice {
        var devic = AVCaptureDevice()
        let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) as NSArray
        for dev in devices {
            devic = dev as AVCaptureDevice
            if devic.position == AVCaptureDevicePosition.Back {
                return devic
            }
        }
        return devic
    }

Event to capture image:

   @IBAction func capture(sender: AnyObject) {
        var videoConnection = AVCaptureConnection()

        for con in stillImageOutput.connections {
            let connection = con as AVCaptureConnection

            for p in connection.inputPorts {
                let port = p as AVCaptureInputPort

                if port.mediaType == AVMediaTypeVideo {
                    videoConnection = connection
                    break;
                }
            }
        }

        stillImageOutput.captureStillImageAsynchronouslyFromConnection(videoConnection) {
            (imageSampleBuffer, error) in
            if imageSampleBuffer != nil {
                let imageData = AVCaptureStillImageOutput .jpegStillImageNSDataRepresentation(imageSampleBuffer)
                self.processImage(UIImage(data: imageData)!)
            }
        }
    }

The processment image:

    func processImage(image: UIImage) {
        UIGraphicsBeginImageContext(CGSizeMake(320, 426));
        image.drawInRect(CGRectMake(0, 0, 320, 426))
        let smallImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext();

        let cropRect = CGRectMake(0, 55, 320, 320)
        let imageRef = CGImageCreateWithImageInRect(smallImage.CGImage, cropRect)
        self.image = UIImage(CGImage: imageRef)
    }

the error occurs right here:

input = AVCaptureDeviceInput(device: self.getBackCamera(), error: error)

回答1:

I've been looking up this issue. It seems that you can't actually instantiate the device AVCaptureDevice object. have to pass it by reference. This is how I overcame the challenge. I didn't instantiate the class I simply passed it in as a reference to the AVCaptureDeviceInput Object.

Hope this helped!

    var captureDevice : AVCaptureDevice?

    let devices = AVCaptureDevice.devices()

    // Loop through all the capture devices on this phone
    for device in devices {
        // Make sure this particular device supports video
        if (device.hasMediaType(AVMediaTypeVideo)) {
            // Finally check the position and confirm we've got the back camera
            if(device.position == AVCaptureDevicePosition.Back) {
                captureDevice = device as? AVCaptureDevice
            }
        }
    }

}


回答2:

Maybe you solve it in the meantime, but if you change your code like this it should work:

Just remove the devic = AVCaptureDevice() in your getBackCamera()

and make the declaration in the head of the class like this: var device, devic : AVCaptureDevice!

class ViewController: UIViewController {

var session: AVCaptureSession!
var device, devic : AVCaptureDevice!
var captureVideoPreviewLayer: AVCaptureVideoPreviewLayer!
var image: UIImage!
var input: AVCaptureDeviceInput!
var stillImageOutput: AVCaptureStillImageOutput!
//var viewControllerScheduling: RentalSchedulingViewController!


override func viewDidLoad() {
    super.viewDidLoad()

    session = AVCaptureSession()
    session.sessionPreset = AVCaptureSessionPresetPhoto;
    captureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
    captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
    captureVideoPreviewLayer.frame = self.view.frame
    self.view.layer.addSublayer(captureVideoPreviewLayer)
    var error = NSErrorPointer()
    captureVideoPreviewLayer.session.beginConfiguration()
    input = AVCaptureDeviceInput(device: self.getBackCamera(), error: error)
    session.addInput(input)
    captureVideoPreviewLayer.session.commitConfiguration()
    stillImageOutput = AVCaptureStillImageOutput()
    var outputSettings = NSDictionary(objectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey)
    stillImageOutput.outputSettings = outputSettings as [NSObject : AnyObject]
    session.addOutput(stillImageOutput)
    session.startRunning()

}

func getBackCamera() -> AVCaptureDevice {
   // devic = AVCaptureDevice()
    let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) as NSArray
    for dev in devices {
        devic = dev as! AVCaptureDevice
        if devic.position == AVCaptureDevicePosition.Back {
            return devic
        }
    }
    return devic
}