Search code examples
swiftxcodeavfoundationpdf417

AVFoundation PDF417 scanner doesn't always work


I am creating an app using Swift 4 and Xcode 9 that scans PDF417 barcodes using AVFoundation. The scanner works with some codes but doesn't recognize the PDF417 barcode that you would find on the front of a CA Lottery scratchers ticket for example.

Is there anything I am missing to make it work? Below is my code:

let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: AVMediaType.video, position: .back)

    guard let captureDevice = deviceDiscoverySession.devices.first else {
        print("Failed to get the camera device")
        return
    }

    do {
        captureSession = AVCaptureSession()

        let input = try AVCaptureDeviceInput(device: captureDevice)
        captureSession!.addInput(input)

        let captureMetadataOutput = AVCaptureMetadataOutput()

        captureSession!.addOutput(captureMetadataOutput)
        captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
        captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.pdf417]

        videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
        videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
        videoPreviewLayer?.frame = view.layer.bounds
        view.layer.addSublayer(videoPreviewLayer!)
        captureSession?.startRunning()
     } catch {
        print(error)
        return
    }

 func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

    //Get the metadata object
    let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
    if scanType.contains(metadataObj.type) {
        let barCodeObj = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)

        if(metadataObj.stringValue != nil) {
            callDelegate(metadataObj.stringValue)
            captureSession?.stopRunning()
            AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
            navigationController?.popViewController(animated: true)
        }
    }
}

Thanks!


Solution

  • Replace your initialization code for the scanner with the following code either in your viewDidLoad or some method that you'd like it to be in

    // Global vars used in init below
    var captureSession: AVCaptureSession!
    var previewLayer: AVCaptureVideoPreviewLayer!
    
    
    func setupCaptureInputDevice() {
        let cameraMediaType = AVMediaType.video
    
        captureSession = AVCaptureSession()
    
        // get the video capture device, which should be of type video
        guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else {
    
            // if there is an error then something is wrong, so dismiss
            dismiss(animated: true, completion: nil)
            return
    
        }
    
        let videoInput: AVCaptureDeviceInput
    
        // create a capture input for the above device input that was created
        do {
            videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
        } catch {
            return
        }
    
        // this is important to check for if we are able to add the input
        // because adding before this could cause a crash or it could not happen
        if (captureSession.canAddInput(videoInput)) {
            captureSession.addInput(videoInput)
        } else {
            // dismiss or display error
            return
        }
    
        // get ready to capture output somewhere
        let metadataOutput = AVCaptureMetadataOutput()
    
        // again check to make sure that we can do this operation before doing it
        if (captureSession.canAddOutput(metadataOutput)) {
            captureSession.addOutput(metadataOutput)
    
            // setting the metadataOutput's delegate to be self and then requesting it run on the main thread
            metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            // specify your code type
            metadataOutput.metadataObjectTypes = [.pdf417]
        } else {
            // dismiss or display error
            return
        }
    
        // the preview layer now becomes the capture session
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    
        // just add it to the screen
        previewLayer.frame = view.layer.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)
    
        // and begin input
        captureSession.startRunning()
    }